diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index 35cf9cb467..34ee4fcfd4 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -94,6 +94,7 @@ import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; import org.thingsboard.server.dao.oauth2.OAuth2ClientService; import org.thingsboard.server.dao.ota.OtaPackageService; +import org.thingsboard.server.dao.pat.ApiKeyService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.relation.RelationService; @@ -572,6 +573,10 @@ public class ActorSystemContext { @Getter private JobManager jobManager; + @Autowired + @Getter + private ApiKeyService apiKeyService; + @Autowired @Getter private OwnerService ownerService; diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index 03830c1c4c..a51eada9d7 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -109,6 +109,7 @@ import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; import org.thingsboard.server.dao.oauth2.OAuth2ClientService; import org.thingsboard.server.dao.ota.OtaPackageService; +import org.thingsboard.server.dao.pat.ApiKeyService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.relation.RelationService; @@ -911,6 +912,11 @@ public class DefaultTbContext implements TbContext { return mainCtx.getJobManager(); } + @Override + public ApiKeyService getApiKeyService() { + return mainCtx.getApiKeyService(); + } + @Override public boolean isExternalNodeForceAck() { return mainCtx.isExternalNodeForceAck(); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java index 6610ecca53..386b482444 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java @@ -627,6 +627,10 @@ public class DefaultTbClusterService implements TbClusterService { // No need to push notifications twice tbRuleEngineServices.removeAll(tbCoreServices); } + if (entityType == EntityType.USER) { + // No need to push user update notification to the rule engine + return; + } for (String serviceId : tbRuleEngineServices) { TopicPartitionInfo tpi = topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceId); ToRuleEngineNotificationMsg toRuleEngineMsg = ToRuleEngineNotificationMsg.newBuilder().setComponentLifecycle(componentLifecycleMsgProto).build(); diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenAuthenticationProvider.java b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenAuthenticationProvider.java index 1aa6e046dd..5017afeb24 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenAuthenticationProvider.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenAuthenticationProvider.java @@ -76,7 +76,7 @@ public class RefreshTokenAuthenticationProvider implements AuthenticationProvide } private SecurityUser authenticateByUserId(UserId userId) { - UserAuthDetails userAuthDetails = userAuthDetailsCache.findUserEnabled(TenantId.SYS_TENANT_ID, userId); + UserAuthDetails userAuthDetails = userAuthDetailsCache.getUserAuthDetails(TenantId.SYS_TENANT_ID, userId); if (userAuthDetails == null) { throw new UsernameNotFoundException("User with credentials not found"); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/pat/ApiKeyAuthenticationProvider.java b/application/src/main/java/org/thingsboard/server/service/security/auth/pat/ApiKeyAuthenticationProvider.java index bcb976e9d8..b72cc4c73e 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/pat/ApiKeyAuthenticationProvider.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/pat/ApiKeyAuthenticationProvider.java @@ -67,7 +67,7 @@ public class ApiKeyAuthenticationProvider implements org.springframework.securit if (apiKey.getExpirationTime() != 0 && apiKey.getExpirationTime() < System.currentTimeMillis()) { throw new CredentialsExpiredException("API key is expired"); } - UserAuthDetails userAuthDetails = userAuthDetailsCache.findUserEnabled(apiKey.getTenantId(), apiKey.getUserId()); + UserAuthDetails userAuthDetails = userAuthDetailsCache.getUserAuthDetails(apiKey.getTenantId(), apiKey.getUserId()); if (userAuthDetails == null) { throw new UsernameNotFoundException("User with credentials not found"); } diff --git a/application/src/main/java/org/thingsboard/server/service/user/cache/DefaultUserAuthDetailsCache.java b/application/src/main/java/org/thingsboard/server/service/user/cache/DefaultUserAuthDetailsCache.java index 33730c7c92..f6c8e7f095 100644 --- a/application/src/main/java/org/thingsboard/server/service/user/cache/DefaultUserAuthDetailsCache.java +++ b/application/src/main/java/org/thingsboard/server/service/user/cache/DefaultUserAuthDetailsCache.java @@ -29,13 +29,13 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.dao.user.UserService; +import org.thingsboard.server.queue.util.TbCoreComponent; import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; @Slf4j @Service +@TbCoreComponent @RequiredArgsConstructor public class DefaultUserAuthDetailsCache implements UserAuthDetailsCache { @@ -47,8 +47,6 @@ public class DefaultUserAuthDetailsCache implements UserAuthDetailsCache { private int cacheValueTtl; private Cache cache; - private final ReadWriteLock lock = new ReentrantReadWriteLock(); - @PostConstruct private void init() { cache = Caffeine.newBuilder() @@ -67,14 +65,9 @@ public class DefaultUserAuthDetailsCache implements UserAuthDetailsCache { } @Override - public UserAuthDetails findUserEnabled(TenantId tenantId, UserId userId) { - lock.readLock().lock(); - try { - log.trace("Retrieving user with enabled credentials status for id {} for tenant {} from cache", userId, tenantId); - return cache.get(userId, id -> userService.findUserAuthDetailsByUserId(tenantId, id)); - } finally { - lock.readLock().unlock(); - } + public UserAuthDetails getUserAuthDetails(TenantId tenantId, UserId userId) { + log.trace("Retrieving user with enabled credentials status for id {} for tenant {} from cache", userId, tenantId); + return cache.get(userId, id -> userService.findUserAuthDetailsByUserId(tenantId, id)); } public void evict(UserId userId) { diff --git a/application/src/main/java/org/thingsboard/server/service/user/cache/UserAuthDetailsCache.java b/application/src/main/java/org/thingsboard/server/service/user/cache/UserAuthDetailsCache.java index 6363ccae67..042d329f71 100644 --- a/application/src/main/java/org/thingsboard/server/service/user/cache/UserAuthDetailsCache.java +++ b/application/src/main/java/org/thingsboard/server/service/user/cache/UserAuthDetailsCache.java @@ -21,6 +21,6 @@ import org.thingsboard.server.common.data.id.UserId; public interface UserAuthDetailsCache { - UserAuthDetails findUserEnabled(TenantId tenantId, UserId userId); + UserAuthDetails getUserAuthDetails(TenantId tenantId, UserId userId); } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 97585e31a7..2276583013 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -437,7 +437,7 @@ sql: ttl: "${SQL_TTL_NOTIFICATIONS_SECS:2592000}" # Default value - 30 days checking_interval_ms: "${SQL_TTL_NOTIFICATIONS_CHECKING_INTERVAL_MS:86400000}" # Default value - 1 day api_keys: - enabled: "${SQL_TTL_API_KEYS_ENABLED:true}" # Enable/disable TTL (Time To Live) for api keys records + enabled: "${SQL_TTL_API_KEYS_ENABLED:true}" # Enable/disable TTL (Time To Live) for expired api keys records checking_interval_ms: "${SQL_TTL_API_KEYS_CHECKING_INTERVAL_MS:86400000}" # Default value - 1 day relations: max_level: "${SQL_RELATIONS_MAX_LEVEL:50}" # This value has to be reasonably small to prevent infinite recursion as early as possible diff --git a/application/src/test/java/org/thingsboard/server/service/security/auth/TokenOutdatingTest.java b/application/src/test/java/org/thingsboard/server/service/security/auth/TokenOutdatingTest.java index 3726a0acff..50d6819f6f 100644 --- a/application/src/test/java/org/thingsboard/server/service/security/auth/TokenOutdatingTest.java +++ b/application/src/test/java/org/thingsboard/server/service/security/auth/TokenOutdatingTest.java @@ -97,7 +97,7 @@ public class TokenOutdatingTest { user.setId(userId); user.setAuthority(Authority.TENANT_ADMIN); user.setEmail("email"); - when(userAuthDetailsCache.findUserEnabled(any(), eq(userId))).thenReturn(new UserAuthDetails(user, true)); + when(userAuthDetailsCache.getUserAuthDetails(any(), eq(userId))).thenReturn(new UserAuthDetails(user, true)); accessTokenAuthenticationProvider = new JwtAuthenticationProvider(tokenFactory, tokenOutdatingService); refreshTokenAuthenticationProvider = new RefreshTokenAuthenticationProvider(tokenFactory, userAuthDetailsCache, mock(CustomerService.class), tokenOutdatingService); diff --git a/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyServiceImpl.java index 9352ce635b..6f1ec5d94c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/pat/ApiKeyServiceImpl.java @@ -90,7 +90,7 @@ public class ApiKeyServiceImpl extends AbstractCachedEntityService INCORRECT_USER_ID + id); return userDao.findUserAuthDetailsByUserId(tenantId.getId(), userId.getId()); } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java index e703a7b256..8df83fb6b6 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java @@ -76,6 +76,7 @@ import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; import org.thingsboard.server.dao.oauth2.OAuth2ClientService; import org.thingsboard.server.dao.ota.OtaPackageService; +import org.thingsboard.server.dao.pat.ApiKeyService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.relation.RelationService; @@ -375,6 +376,8 @@ public interface TbContext { JobManager getJobManager(); + ApiKeyService getApiKeyService(); + boolean isExternalNodeForceAck(); /** diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java index 6e4b56ccd5..55691039a3 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java @@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.cf.CalculatedFieldLink; import org.thingsboard.server.common.data.id.AiModelId; import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.ApiKeyId; import org.thingsboard.server.common.data.id.ApiUsageStateId; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.AssetProfileId; @@ -148,7 +149,6 @@ public class TenantIdLoader { break; case NOTIFICATION: case ADMIN_SETTINGS: - case API_KEY: return ctxTenantId; case NOTIFICATION_RULE: tenantEntity = ctx.getNotificationRuleService().findNotificationRuleById(ctxTenantId, new NotificationRuleId(id)); @@ -185,6 +185,9 @@ public class TenantIdLoader { case AI_MODEL: tenantEntity = ctx.getAiModelService().findAiModelById(ctxTenantId, new AiModelId(id)).orElse(null); break; + case API_KEY: + tenantEntity = ctx.getApiKeyService().findApiKeyById(ctxTenantId, new ApiKeyId(id)); + break; default: throw new RuntimeException("Unexpected entity type: " + entityId.getEntityType()); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/TenantIdLoaderTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/TenantIdLoaderTest.java index f973bad105..13bbfa2eba 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/TenantIdLoaderTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/TenantIdLoaderTest.java @@ -62,6 +62,7 @@ import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; import org.thingsboard.server.common.data.oauth2.OAuth2Client; +import org.thingsboard.server.common.data.pat.ApiKey; import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.data.queue.QueueStats; import org.thingsboard.server.common.data.rpc.Rpc; @@ -169,6 +170,8 @@ public class TenantIdLoaderTest { private JobService jobService; @Mock private AiModelService aiModelService; + @Mock + private ApiKeyService apiKeyService; private TenantId tenantId; private TenantProfileId tenantProfileId; @@ -203,7 +206,6 @@ public class TenantIdLoaderTest { case TENANT: case NOTIFICATION: case ADMIN_SETTINGS: - case API_KEY: break; case CUSTOMER: Customer customer = new Customer(); @@ -400,6 +402,12 @@ public class TenantIdLoaderTest { when(ctx.getAiModelService()).thenReturn(aiModelService); doReturn(Optional.of(aiModel)).when(aiModelService).findAiModelById(eq(tenantId), any()); break; + case API_KEY: + ApiKey apiKey = new ApiKey(); + apiKey.setTenantId(tenantId); + when(ctx.getApiKeyService()).thenReturn(apiKeyService); + doReturn(apiKey).when(apiKeyService).findApiKeyById(eq(tenantId), any()); + break; default: throw new RuntimeException("Unexpected originator EntityType " + entityType); }