From 1b9d70f2fe6ace5d1a9b6d7b7ed34869d9924262 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Mon, 14 Feb 2022 16:57:38 +0200 Subject: [PATCH 1/3] Refactoring validators - moved them to a separate classes --- .../server/controller/EdgeController.java | 2 +- .../edge/DefaultEdgeNotificationService.java | 4 +- .../service/edge/EdgeBulkImportService.java | 2 +- .../edge/rpc/processor/BaseEdgeProcessor.java | 7 +- .../rpc/processor/DeviceEdgeProcessor.java | 11 +- .../server/dao/edge/EdgeService.java | 2 +- .../server/dao/alarm/BaseAlarmService.java | 37 +- .../server/dao/asset/BaseAssetService.java | 85 +-- .../server/dao/audit/AuditLogServiceImpl.java | 21 +- .../dao/cache/EntitiesCacheManager.java | 30 + .../dao/cache/EntitiesCacheManagerImpl.java | 64 +++ .../BaseComponentDescriptorService.java | 24 +- .../dao/customer/CustomerServiceImpl.java | 66 +-- .../dao/dashboard/DashboardServiceImpl.java | 39 +- .../device/DeviceCredentialsServiceImpl.java | 55 +- .../dao/device/DeviceProfileServiceImpl.java | 490 +--------------- .../server/dao/device/DeviceServiceImpl.java | 147 +---- .../server/dao/edge/BaseEdgeEventService.java | 17 +- .../server/dao/edge/EdgeServiceImpl.java | 93 +--- .../dao/entityview/EntityViewServiceImpl.java | 67 +-- .../server/dao/event/BaseEventService.java | 19 +- .../OAuth2ConfigTemplateServiceImpl.java | 44 +- .../server/dao/ota/BaseOtaPackageService.java | 180 +----- .../dao/resource/BaseResourceService.java | 60 +- .../server/dao/rule/BaseRuleChainService.java | 69 +-- .../validator/AdminSettingsDataValidator.java | 61 ++ .../service/validator/AlarmDataValidator.java | 57 ++ .../validator/ApiUsageDataValidator.java | 50 ++ .../service/validator/AssetDataValidator.java | 108 ++++ .../validator/AuditLogDataValidator.java | 39 ++ .../BaseOtaPackageDataValidator.java | 123 ++++ ...ientRegistrationTemplateDataValidator.java | 51 ++ .../ComponentDescriptorDataValidator.java | 43 ++ .../validator/CustomerDataValidator.java | 92 +++ .../validator/DashboardDataValidator.java | 68 +++ .../DeviceCredentialsDataValidator.java | 75 +++ .../validator/DeviceDataValidator.java | 147 +++++ .../validator/DeviceProfileDataValidator.java | 524 ++++++++++++++++++ .../service/validator/EdgeDataValidator.java | 90 +++ .../validator/EdgeEventDataValidator.java | 36 ++ .../validator/EntityViewDataValidator.java | 88 +++ .../service/validator/EventDataValidator.java | 40 ++ .../validator/OtaPackageDataValidator.java | 105 ++++ .../OtaPackageInfoDataValidator.java | 40 ++ .../validator/ResourceDataValidator.java | 82 +++ .../validator/RuleChainDataValidator.java | 88 +++ .../validator/TenantDataValidator.java | 68 +++ .../validator/TenantProfileDataValidator.java | 69 +++ .../UserCredentialsDataValidator.java | 68 +++ .../service/validator/UserDataValidator.java | 136 +++++ .../validator/WidgetTypeDataValidator.java | 98 ++++ .../validator/WidgetsBundleDataValidator.java | 81 +++ .../settings/AdminSettingsServiceImpl.java | 38 +- .../dao/tenant/TenantProfileServiceImpl.java | 50 +- .../server/dao/tenant/TenantServiceImpl.java | 39 +- .../usagerecord/ApiUsageStateServiceImpl.java | 31 +- .../server/dao/user/UserServiceImpl.java | 141 +---- .../dao/widget/WidgetTypeServiceImpl.java | 71 +-- .../dao/widget/WidgetsBundleServiceImpl.java | 59 +- .../dao/service/BaseDashboardServiceTest.java | 2 +- .../BaseDeviceCredentialsCacheTest.java | 12 +- .../dao/service/BaseEdgeServiceTest.java | 48 +- .../dao/service/BaseEntityServiceTest.java | 8 +- .../dao/service/BaseRuleChainServiceTest.java | 2 +- 64 files changed, 2784 insertions(+), 1879 deletions(-) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/cache/EntitiesCacheManager.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/cache/EntitiesCacheManagerImpl.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/AdminSettingsDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/AlarmDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/ApiUsageDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/AuditLogDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/BaseOtaPackageDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/ClientRegistrationTemplateDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/ComponentDescriptorDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/CustomerDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/DashboardDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceCredentialsDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/EdgeDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/EdgeEventDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/EntityViewDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/EventDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/OtaPackageDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/OtaPackageInfoDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/RuleChainDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/UserCredentialsDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/UserDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetTypeDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetsBundleDataValidator.java diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java index 5f79efaffa..f3d73f13e5 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java @@ -169,7 +169,7 @@ public class EdgeController extends BaseController { accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation, edge.getId(), edge); - Edge savedEdge = checkNotNull(edgeService.saveEdge(edge, true)); + Edge savedEdge = checkNotNull(edgeService.saveEdge(edge)); onEdgeCreatedOrUpdated(tenantId, savedEdge, edgeTemplateRootRuleChain, !created, getCurrentUser()); return savedEdge; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java index 8309fa7ef6..68fecd47fc 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java @@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEventActionType; @@ -38,7 +39,6 @@ import org.thingsboard.server.service.edge.rpc.processor.CustomerEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.EdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.EntityEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.RelationEdgeProcessor; -import org.thingsboard.server.cluster.TbClusterService; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @@ -93,7 +93,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { @Override public Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException { edge.setRootRuleChainId(ruleChainId); - Edge savedEdge = edgeService.saveEdge(edge, true); + Edge savedEdge = edgeService.saveEdge(edge); saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN, EdgeEventActionType.UPDATED, ruleChainId, null); return savedEdge; } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java index ff393a2b3d..615a7cddd2 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java @@ -68,7 +68,7 @@ public class EdgeBulkImportService extends AbstractBulkImportService { @Override protected Edge saveEntity(Edge entity, Map fields) { - return edgeService.saveEdge(entity, true); + return edgeService.saveEdge(entity); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java index ac261fef91..10d1ae88c5 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java @@ -19,6 +19,8 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.HasCustomerId; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; @@ -43,6 +45,7 @@ import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.dao.widget.WidgetsBundleService; @@ -62,7 +65,6 @@ import org.thingsboard.server.service.edge.rpc.constructor.WidgetTypeMsgConstruc import org.thingsboard.server.service.edge.rpc.constructor.WidgetsBundleMsgConstructor; import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.profile.TbDeviceProfileCache; -import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.service.state.DeviceStateService; @Slf4j @@ -129,6 +131,9 @@ public abstract class BaseEdgeProcessor { @Autowired protected WidgetTypeService widgetTypeService; + @Autowired + protected DataValidator deviceValidator; + @Autowired protected EntityDataMsgConstructor entityDataMsgConstructor; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceEdgeProcessor.java index 20c8ac7933..8a6697d7b1 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceEdgeProcessor.java @@ -26,7 +26,6 @@ import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.rpc.RpcError; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; @@ -45,11 +44,13 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.rpc.RpcError; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg; @@ -59,7 +60,6 @@ import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.queue.TbQueueMsgMetadata; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; import org.thingsboard.server.service.rpc.FromDeviceRpcResponseActorMsg; import java.util.UUID; @@ -194,7 +194,6 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor { if (device == null) { device = new Device(); device.setTenantId(tenantId); - device.setId(deviceId); device.setCreatedTime(Uuids.unixTimestamp(deviceId.getId())); created = true; } @@ -214,6 +213,12 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor { deviceUpdateMsg.getDeviceProfileIdLSB())); device.setDeviceProfileId(deviceProfileId); } + if (created) { + deviceValidator.validate(device, Device::getTenantId); + device.setId(deviceId); + } else { + deviceValidator.validate(device, Device::getTenantId); + } Device savedDevice = deviceService.saveDevice(device, false); tbClusterService.onDeviceUpdated(savedDevice, created ? null : device, false); if (created) { diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java index 51b4468d01..ea95923275 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java @@ -42,7 +42,7 @@ public interface EdgeService { Optional findEdgeByRoutingKey(TenantId tenantId, String routingKey); - Edge saveEdge(Edge edge, boolean doValidate); + Edge saveEdge(Edge edge); Edge assignEdgeToCustomer(TenantId tenantId, EdgeId edgeId, CustomerId customerId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java index 08f4576449..714e131025 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java @@ -25,9 +25,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; import org.thingsboard.common.util.ThingsBoardThreadFactory; -import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmQuery; @@ -49,9 +47,7 @@ import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.RelationsSearchParameters; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entity.EntityService; -import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; -import org.thingsboard.server.dao.tenant.TenantDao; import javax.annotation.Nullable; import javax.annotation.PostConstruct; @@ -59,7 +55,6 @@ import javax.annotation.PreDestroy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -82,10 +77,10 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ private AlarmDao alarmDao; @Autowired - private TenantDao tenantDao; + private EntityService entityService; @Autowired - private EntityService entityService; + private DataValidator alarmDataValidator; protected ExecutorService readResultsProcessingExecutor; @@ -412,32 +407,4 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ ListenableFuture entity = alarmDao.findAlarmByIdAsync(tenantId, alarmId.getId()); return Futures.transform(entity, function, readResultsProcessingExecutor); } - - private DataValidator alarmDataValidator = - new DataValidator<>() { - - @Override - protected void validateDataImpl(TenantId tenantId, Alarm alarm) { - if (StringUtils.isEmpty(alarm.getType())) { - throw new DataValidationException("Alarm type should be specified!"); - } - if (alarm.getOriginator() == null) { - throw new DataValidationException("Alarm originator should be specified!"); - } - if (alarm.getSeverity() == null) { - throw new DataValidationException("Alarm severity should be specified!"); - } - if (alarm.getStatus() == null) { - throw new DataValidationException("Alarm status should be specified!"); - } - if (alarm.getTenantId() == null) { - throw new DataValidationException("Alarm should be assigned to tenant!"); - } else { - Tenant tenant = tenantDao.findById(alarm.getTenantId(), alarm.getTenantId().getId()); - if (tenant == null) { - throw new DataValidationException("Alarm is referencing to non-existent tenant!"); - } - } - } - }; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java index e92f1fd9ce..bf8d75d27a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java @@ -22,18 +22,12 @@ import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; import org.hibernate.exception.ConstraintViolationException; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; -import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; -import org.springframework.util.StringUtils; -import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; -import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetInfo; import org.thingsboard.server.common.data.asset.AssetSearchQuery; @@ -48,17 +42,13 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.RelationTypeGroup; -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; -import org.thingsboard.server.dao.customer.CustomerDao; +import org.thingsboard.server.dao.cache.EntitiesCacheManager; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; -import org.thingsboard.server.dao.tenant.TbTenantProfileCache; -import org.thingsboard.server.dao.tenant.TenantDao; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -67,7 +57,6 @@ import java.util.stream.Collectors; import static org.thingsboard.server.common.data.CacheConstants.ASSET_CACHE; import static org.thingsboard.server.dao.DaoUtil.toUUIDs; -import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validateIds; import static org.thingsboard.server.dao.service.Validator.validatePageLink; @@ -86,17 +75,10 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ private AssetDao assetDao; @Autowired - private TenantDao tenantDao; + private EntitiesCacheManager cacheManager; @Autowired - private CustomerDao customerDao; - - @Autowired - private CacheManager cacheManager; - - @Autowired - @Lazy - private TbTenantProfileCache tenantProfileCache; + private DataValidator assetValidator; @Override public AssetInfo findAssetInfoById(TenantId tenantId, AssetId assetId) { @@ -178,16 +160,11 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ throw new RuntimeException("Exception while finding entity views for assetId [" + assetId + "]", e); } - removeAssetFromCacheByName(asset.getTenantId(), asset.getName()); + cacheManager.removeAssetFromCacheByName(asset.getTenantId(), asset.getName()); assetDao.removeById(tenantId, assetId.getId()); } - private void removeAssetFromCacheByName(TenantId tenantId, String name) { - Cache cache = cacheManager.getCache(ASSET_CACHE); - cache.evict(Arrays.asList(tenantId, name)); - } - @Override public PageData findAssetsByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findAssetsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); @@ -381,60 +358,6 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ return assetDao.findAssetsByTenantIdAndEdgeIdAndType(tenantId.getId(), edgeId.getId(), type, pageLink); } - private DataValidator assetValidator = - new DataValidator() { - - @Override - protected void validateCreate(TenantId tenantId, Asset asset) { - DefaultTenantProfileConfiguration profileConfiguration = - (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); - if (!TB_SERVICE_QUEUE.equals(asset.getType())) { - long maxAssets = profileConfiguration.getMaxAssets(); - validateNumberOfEntitiesPerTenant(tenantId, assetDao, maxAssets, EntityType.ASSET); - } - } - - @Override - protected void validateUpdate(TenantId tenantId, Asset asset) { - Asset old = assetDao.findById(asset.getTenantId(), asset.getId().getId()); - if (old == null) { - throw new DataValidationException("Can't update non existing asset!"); - } - if (!old.getName().equals(asset.getName())) { - removeAssetFromCacheByName(tenantId, old.getName()); - } - } - - @Override - protected void validateDataImpl(TenantId tenantId, Asset asset) { - if (StringUtils.isEmpty(asset.getType())) { - throw new DataValidationException("Asset type should be specified!"); - } - if (StringUtils.isEmpty(asset.getName())) { - throw new DataValidationException("Asset name should be specified!"); - } - if (asset.getTenantId() == null) { - throw new DataValidationException("Asset should be assigned to tenant!"); - } else { - Tenant tenant = tenantDao.findById(tenantId, asset.getTenantId().getId()); - if (tenant == null) { - throw new DataValidationException("Asset is referencing to non-existent tenant!"); - } - } - if (asset.getCustomerId() == null) { - asset.setCustomerId(new CustomerId(NULL_UUID)); - } else if (!asset.getCustomerId().getId().equals(NULL_UUID)) { - Customer customer = customerDao.findById(tenantId, asset.getCustomerId().getId()); - if (customer == null) { - throw new DataValidationException("Can't assign asset to non-existent customer!"); - } - if (!customer.getTenantId().equals(asset.getTenantId())) { - throw new DataValidationException("Can't assign asset to customer from different tenant!"); - } - } - } - }; - private PaginatedRemover tenantAssetsRemover = new PaginatedRemover() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java index bfd591ffa4..6e26f4343d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java @@ -27,6 +27,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.audit.ActionStatus; @@ -47,9 +48,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.dao.audit.sink.AuditLogSink; import org.thingsboard.server.dao.device.provision.ProvisionRequest; import org.thingsboard.server.dao.entity.EntityService; -import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; -import org.thingsboard.common.util.JacksonUtil; import java.io.PrintWriter; import java.io.StringWriter; @@ -80,6 +79,9 @@ public class AuditLogServiceImpl implements AuditLogService { @Autowired private AuditLogSink auditLogSink; + @Autowired + private DataValidator auditLogValidator; + @Override public PageData findAuditLogsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, List actionTypes, TimePageLink pageLink) { log.trace("Executing findAuditLogsByTenantIdAndCustomerId [{}], [{}], [{}]", tenantId, customerId, pageLink); @@ -389,19 +391,4 @@ public class AuditLogServiceImpl implements AuditLogService { return Futures.allAsList(futures); } - private DataValidator auditLogValidator = - new DataValidator() { - @Override - protected void validateDataImpl(TenantId tenantId, AuditLog auditLog) { - if (auditLog.getEntityId() == null) { - throw new DataValidationException("Entity Id should be specified!"); - } - if (auditLog.getTenantId() == null) { - throw new DataValidationException("Tenant Id should be specified!"); - } - if (auditLog.getUserId() == null) { - throw new DataValidationException("User Id should be specified!"); - } - } - }; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/cache/EntitiesCacheManager.java b/dao/src/main/java/org/thingsboard/server/dao/cache/EntitiesCacheManager.java new file mode 100644 index 0000000000..19842eacc4 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/cache/EntitiesCacheManager.java @@ -0,0 +1,30 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.cache; + +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; + +public interface EntitiesCacheManager { + + void removeDeviceFromCacheByName(TenantId tenantId, String name); + + void removeDeviceFromCacheById(TenantId tenantId, DeviceId deviceId); + + void removeAssetFromCacheByName(TenantId tenantId, String name); + + void removeEdgeFromCacheByName(TenantId tenantId, String name); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/cache/EntitiesCacheManagerImpl.java b/dao/src/main/java/org/thingsboard/server/dao/cache/EntitiesCacheManagerImpl.java new file mode 100644 index 0000000000..2a28f07424 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/cache/EntitiesCacheManagerImpl.java @@ -0,0 +1,64 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.cache; + +import lombok.AllArgsConstructor; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; + +import java.util.Arrays; + +import static org.thingsboard.server.common.data.CacheConstants.ASSET_CACHE; +import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CACHE; +import static org.thingsboard.server.common.data.CacheConstants.EDGE_CACHE; + +@Component +@AllArgsConstructor +public class EntitiesCacheManagerImpl implements EntitiesCacheManager { + + private final CacheManager cacheManager; + + @Override + public void removeDeviceFromCacheByName(TenantId tenantId, String name) { + Cache cache = cacheManager.getCache(DEVICE_CACHE); + cache.evict(Arrays.asList(tenantId, name)); + } + + @Override + public void removeDeviceFromCacheById(TenantId tenantId, DeviceId deviceId) { + if (deviceId == null) { + return; + } + Cache cache = cacheManager.getCache(DEVICE_CACHE); + cache.evict(Arrays.asList(tenantId, deviceId)); + } + + @Override + public void removeAssetFromCacheByName(TenantId tenantId, String name) { + Cache cache = cacheManager.getCache(ASSET_CACHE); + cache.evict(Arrays.asList(tenantId, name)); + } + + @Override + public void removeEdgeFromCacheByName(TenantId tenantId, String name) { + Cache cache = cacheManager.getCache(EDGE_CACHE); + cache.evict(Arrays.asList(tenantId, name)); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java b/dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java index 845665f0c4..3f6ee3cbb0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java @@ -21,11 +21,9 @@ import com.github.fge.jsonschema.core.report.ProcessingReport; import com.github.fge.jsonschema.main.JsonSchemaFactory; import com.github.fge.jsonschema.main.JsonValidator; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.id.ComponentDescriptorId; -import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -49,6 +47,9 @@ public class BaseComponentDescriptorService implements ComponentDescriptorServic @Autowired private ComponentDescriptorDao componentDescriptorDao; + @Autowired + private DataValidator componentValidator; + @Override public ComponentDescriptor saveComponent(TenantId tenantId, ComponentDescriptor component) { componentValidator.validate(component, data -> TenantId.SYS_TENANT_ID); @@ -100,23 +101,4 @@ public class BaseComponentDescriptorService implements ComponentDescriptorServic throw new IncorrectParameterException(e.getMessage(), e); } } - - private DataValidator componentValidator = - new DataValidator() { - @Override - protected void validateDataImpl(TenantId tenantId, ComponentDescriptor plugin) { - if (plugin.getType() == null) { - throw new DataValidationException("Component type should be specified!"); - } - if (plugin.getScope() == null) { - throw new DataValidationException("Component scope should be specified!"); - } - if (StringUtils.isEmpty(plugin.getName())) { - throw new DataValidationException("Component name should be specified!"); - } - if (StringUtils.isEmpty(plugin.getClazz())) { - throw new DataValidationException("Component clazz should be specified!"); - } - } - }; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java index 1b21569d54..7f247f5ba2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java @@ -19,30 +19,22 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entityview.EntityViewService; -import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; -import org.thingsboard.server.dao.tenant.TbTenantProfileCache; -import org.thingsboard.server.dao.tenant.TenantDao; import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; import org.thingsboard.server.dao.user.UserService; @@ -55,7 +47,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @Slf4j public class CustomerServiceImpl extends AbstractEntityService implements CustomerService { - private static final String PUBLIC_CUSTOMER_TITLE = "Public"; + public static final String PUBLIC_CUSTOMER_TITLE = "Public"; public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId "; public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; @@ -65,9 +57,6 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom @Autowired private UserService userService; - @Autowired - private TenantDao tenantDao; - @Autowired private AssetService assetService; @@ -84,8 +73,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom private ApiUsageStateService apiUsageStateService; @Autowired - @Lazy - private TbTenantProfileCache tenantProfileCache; + private DataValidator customerValidator; @Override public Customer findCustomerById(TenantId tenantId, CustomerId customerId) { @@ -171,56 +159,6 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom customersByTenantRemover.removeEntities(tenantId, tenantId); } - private DataValidator customerValidator = - new DataValidator() { - - @Override - protected void validateCreate(TenantId tenantId, Customer customer) { - DefaultTenantProfileConfiguration profileConfiguration = - (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); - long maxCustomers = profileConfiguration.getMaxCustomers(); - - validateNumberOfEntitiesPerTenant(tenantId, customerDao, maxCustomers, EntityType.CUSTOMER); - customerDao.findCustomersByTenantIdAndTitle(customer.getTenantId().getId(), customer.getTitle()).ifPresent( - c -> { - throw new DataValidationException("Customer with such title already exists!"); - } - ); - } - - @Override - protected void validateUpdate(TenantId tenantId, Customer customer) { - customerDao.findCustomersByTenantIdAndTitle(customer.getTenantId().getId(), customer.getTitle()).ifPresent( - c -> { - if (!c.getId().equals(customer.getId())) { - throw new DataValidationException("Customer with such title already exists!"); - } - } - ); - } - - @Override - protected void validateDataImpl(TenantId tenantId, Customer customer) { - if (StringUtils.isEmpty(customer.getTitle())) { - throw new DataValidationException("Customer title should be specified!"); - } - if (customer.getTitle().equals(PUBLIC_CUSTOMER_TITLE)) { - throw new DataValidationException("'Public' title for customer is system reserved!"); - } - if (!StringUtils.isEmpty(customer.getEmail())) { - validateEmail(customer.getEmail()); - } - if (customer.getTenantId() == null) { - throw new DataValidationException("Customer should be assigned to tenant!"); - } else { - Tenant tenant = tenantDao.findById(tenantId, customer.getTenantId().getId()); - if (tenant == null) { - throw new DataValidationException("Customer is referencing to non-existent tenant!"); - } - } - } - }; - private PaginatedRemover customersByTenantRemover = new PaginatedRemover() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java index f89c1b0450..ee5c44e20f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java @@ -17,16 +17,12 @@ package org.thingsboard.server.dao.dashboard; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.hibernate.exception.ConstraintViolationException; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.DashboardInfo; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; @@ -36,7 +32,6 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.edge.EdgeDao; import org.thingsboard.server.dao.entity.AbstractEntityService; @@ -44,8 +39,6 @@ import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; -import org.thingsboard.server.dao.tenant.TbTenantProfileCache; -import org.thingsboard.server.dao.tenant.TenantDao; import static org.thingsboard.server.dao.service.Validator.validateId; @@ -61,9 +54,6 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Autowired private DashboardInfoDao dashboardInfoDao; - @Autowired - private TenantDao tenantDao; - @Autowired private CustomerDao customerDao; @@ -71,8 +61,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb private EdgeDao edgeDao; @Autowired - @Lazy - private TbTenantProfileCache tenantProfileCache; + private DataValidator dashboardValidator; @Override public Dashboard findDashboardById(TenantId tenantId, DashboardId dashboardId) { @@ -290,32 +279,6 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb return dashboardInfoDao.findFirstByTenantIdAndName(tenantId.getId(), name); } - private DataValidator dashboardValidator = - new DataValidator() { - @Override - protected void validateCreate(TenantId tenantId, Dashboard data) { - DefaultTenantProfileConfiguration profileConfiguration = - (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); - long maxDashboards = profileConfiguration.getMaxDashboards(); - validateNumberOfEntitiesPerTenant(tenantId, dashboardDao, maxDashboards, EntityType.DASHBOARD); - } - - @Override - protected void validateDataImpl(TenantId tenantId, Dashboard dashboard) { - if (StringUtils.isEmpty(dashboard.getTitle())) { - throw new DataValidationException("Dashboard title should be specified!"); - } - if (dashboard.getTenantId() == null) { - throw new DataValidationException("Dashboard should be assigned to tenant!"); - } else { - Tenant tenant = tenantDao.findById(tenantId, dashboard.getTenantId().getId()); - if (tenant == null) { - throw new DataValidationException("Dashboard is referencing to non-existent tenant!"); - } - } - } - }; - private PaginatedRemover tenantDashboardsRemover = new PaginatedRemover() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java index 1be93dd7b9..ff92c330e0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java @@ -25,21 +25,19 @@ import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; +import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MBootstrapClientCredential; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MBootstrapClientCredentials; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredential; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials; -import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MBootstrapClientCredential; -import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredential; import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKBootstrapClientCredential; -import org.thingsboard.server.common.data.device.credentials.lwm2m.RPKClientCredential; +import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredential; import org.thingsboard.server.common.data.device.credentials.lwm2m.RPKBootstrapClientCredential; -import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredential; +import org.thingsboard.server.common.data.device.credentials.lwm2m.RPKClientCredential; import org.thingsboard.server.common.data.device.credentials.lwm2m.X509BootstrapClientCredential; +import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredential; import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.msg.EncryptionUtil; @@ -47,6 +45,7 @@ import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; import org.thingsboard.server.dao.service.DataValidator; + import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CREDENTIALS_CACHE; import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validateString; @@ -59,7 +58,7 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen private DeviceCredentialsDao deviceCredentialsDao; @Autowired - private DeviceService deviceService; + private DataValidator credentialsValidator; @Override public DeviceCredentials findDeviceCredentialsByDeviceId(TenantId tenantId, DeviceId deviceId) { @@ -375,46 +374,4 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen deviceCredentialsDao.removeById(tenantId, deviceCredentials.getUuidId()); } - private DataValidator credentialsValidator = - new DataValidator() { - - @Override - protected void validateCreate(TenantId tenantId, DeviceCredentials deviceCredentials) { - if (deviceCredentialsDao.findByDeviceId(tenantId, deviceCredentials.getDeviceId().getId()) != null) { - throw new DeviceCredentialsValidationException("Credentials for this device are already specified!"); - } - if (deviceCredentialsDao.findByCredentialsId(tenantId, deviceCredentials.getCredentialsId()) != null) { - throw new DeviceCredentialsValidationException("Device credentials are already assigned to another device!"); - } - } - - @Override - protected void validateUpdate(TenantId tenantId, DeviceCredentials deviceCredentials) { - if (deviceCredentialsDao.findById(tenantId, deviceCredentials.getUuidId()) == null) { - throw new DeviceCredentialsValidationException("Unable to update non-existent device credentials!"); - } - DeviceCredentials existingCredentials = deviceCredentialsDao.findByCredentialsId(tenantId, deviceCredentials.getCredentialsId()); - if (existingCredentials != null && !existingCredentials.getId().equals(deviceCredentials.getId())) { - throw new DeviceCredentialsValidationException("Device credentials are already assigned to another device!"); - } - } - - @Override - protected void validateDataImpl(TenantId tenantId, DeviceCredentials deviceCredentials) { - if (deviceCredentials.getDeviceId() == null) { - throw new DeviceCredentialsValidationException("Device credentials should be assigned to device!"); - } - if (deviceCredentials.getCredentialsType() == null) { - throw new DeviceCredentialsValidationException("Device credentials type should be specified!"); - } - if (StringUtils.isEmpty(deviceCredentials.getCredentialsId())) { - throw new DeviceCredentialsValidationException("Device credentials id should be specified!"); - } - Device device = deviceService.findDeviceById(tenantId, deviceCredentials.getDeviceId()); - if (device == null) { - throw new DeviceCredentialsValidationException("Can't assign device credentials to non-existent device!"); - } - } - }; - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java index 9a272b4739..602128818b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java @@ -15,83 +15,37 @@ */ package org.thingsboard.server.dao.device; -import com.google.protobuf.Descriptors; -import com.google.protobuf.DynamicMessage; -import com.squareup.wire.Syntax; -import com.squareup.wire.schema.Field; -import com.squareup.wire.schema.Location; -import com.squareup.wire.schema.internal.parser.EnumElement; -import com.squareup.wire.schema.internal.parser.FieldElement; -import com.squareup.wire.schema.internal.parser.MessageElement; -import com.squareup.wire.schema.internal.parser.OneOfElement; -import com.squareup.wire.schema.internal.parser.ProtoFileElement; -import com.squareup.wire.schema.internal.parser.ProtoParser; -import com.squareup.wire.schema.internal.parser.TypeElement; import lombok.extern.slf4j.Slf4j; -import org.eclipse.leshan.core.util.SecurityUtil; -import org.thingsboard.server.common.data.StringUtils; import org.hibernate.exception.ConstraintViolationException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; -import org.springframework.util.CollectionUtils; -import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileInfo; import org.thingsboard.server.common.data.DeviceProfileProvisionType; import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceTransportType; -import org.thingsboard.server.common.data.OtaPackage; -import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode; -import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; -import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration; -import org.thingsboard.server.common.data.device.profile.DefaultCoapDeviceTypeConfiguration; import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; -import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; -import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; -import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; -import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; -import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; -import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; -import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.AbstractLwM2MBootstrapServerCredential; -import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.RPKLwM2MBootstrapServerCredential; -import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServerCredential; -import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.X509LwM2MBootstrapServerCredential; -import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.common.data.rule.RuleChain; -import org.thingsboard.server.common.msg.EncryptionUtil; -import org.thingsboard.server.common.msg.queue.ServiceType; -import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; -import org.thingsboard.server.dao.ota.OtaPackageService; -import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; -import org.thingsboard.server.dao.tenant.TenantDao; -import org.thingsboard.server.queue.QueueService; import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import java.util.stream.Collectors; import static org.thingsboard.server.common.data.CacheConstants.DEVICE_PROFILE_CACHE; import static org.thingsboard.server.dao.service.Validator.validateId; @@ -104,19 +58,6 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D private static final String INCORRECT_DEVICE_PROFILE_ID = "Incorrect deviceProfileId "; private static final String INCORRECT_DEVICE_PROFILE_NAME = "Incorrect deviceProfileName "; - private static final Location LOCATION = new Location("", "", -1, -1); - private static final String ATTRIBUTES_PROTO_SCHEMA = "attributes proto schema"; - private static final String TELEMETRY_PROTO_SCHEMA = "telemetry proto schema"; - private static final String RPC_REQUEST_PROTO_SCHEMA = "rpc request proto schema"; - private static final String RPC_RESPONSE_PROTO_SCHEMA = "rpc response proto schema"; - - private static String invalidSchemaProvidedMessage(String schemaName) { - return "[Transport Configuration] invalid " + schemaName + " provided!"; - } - - @Autowired(required = false) - private QueueService queueService; - @Autowired private DeviceProfileDao deviceProfileDao; @@ -126,20 +67,11 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D @Autowired private DeviceService deviceService; - @Autowired - private TenantDao tenantDao; - @Autowired private CacheManager cacheManager; @Autowired - private OtaPackageService otaPackageService; - - @Autowired - private RuleChainService ruleChainService; - - @Autowired - private DashboardService dashboardService; + private DataValidator deviceProfileValidator; private final Lock findOrCreateLock = new ReentrantLock(); @@ -359,426 +291,6 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D tenantDeviceProfilesRemover.removeEntities(tenantId, tenantId); } - private DataValidator deviceProfileValidator = - new DataValidator<>() { - @Override - protected void validateDataImpl(TenantId tenantId, DeviceProfile deviceProfile) { - if (StringUtils.isEmpty(deviceProfile.getName())) { - throw new DataValidationException("Device profile name should be specified!"); - } - if (deviceProfile.getType() == null) { - throw new DataValidationException("Device profile type should be specified!"); - } - if (deviceProfile.getTransportType() == null) { - throw new DataValidationException("Device profile transport type should be specified!"); - } - if (deviceProfile.getTenantId() == null) { - throw new DataValidationException("Device profile should be assigned to tenant!"); - } else { - Tenant tenant = tenantDao.findById(deviceProfile.getTenantId(), deviceProfile.getTenantId().getId()); - if (tenant == null) { - throw new DataValidationException("Device profile is referencing to non-existent tenant!"); - } - } - if (deviceProfile.isDefault()) { - DeviceProfile defaultDeviceProfile = findDefaultDeviceProfile(tenantId); - if (defaultDeviceProfile != null && !defaultDeviceProfile.getId().equals(deviceProfile.getId())) { - throw new DataValidationException("Another default device profile is present in scope of current tenant!"); - } - } - if (!StringUtils.isEmpty(deviceProfile.getDefaultQueueName()) && queueService != null){ - if(!queueService.getQueuesByServiceType(ServiceType.TB_RULE_ENGINE).contains(deviceProfile.getDefaultQueueName())){ - throw new DataValidationException("Device profile is referencing to non-existent queue!"); - } - } - if (deviceProfile.getProvisionType() == null) { - deviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED); - } - DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); - transportConfiguration.validate(); - if (transportConfiguration instanceof MqttDeviceProfileTransportConfiguration) { - MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration; - if (mqttTransportConfiguration.getTransportPayloadTypeConfiguration() instanceof ProtoTransportPayloadConfiguration) { - ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = - (ProtoTransportPayloadConfiguration) mqttTransportConfiguration.getTransportPayloadTypeConfiguration(); - validateProtoSchemas(protoTransportPayloadConfiguration); - validateTelemetryDynamicMessageFields(protoTransportPayloadConfiguration); - validateRpcRequestDynamicMessageFields(protoTransportPayloadConfiguration); - } - } else if (transportConfiguration instanceof CoapDeviceProfileTransportConfiguration) { - CoapDeviceProfileTransportConfiguration coapDeviceProfileTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration; - CoapDeviceTypeConfiguration coapDeviceTypeConfiguration = coapDeviceProfileTransportConfiguration.getCoapDeviceTypeConfiguration(); - if (coapDeviceTypeConfiguration instanceof DefaultCoapDeviceTypeConfiguration) { - DefaultCoapDeviceTypeConfiguration defaultCoapDeviceTypeConfiguration = (DefaultCoapDeviceTypeConfiguration) coapDeviceTypeConfiguration; - TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = defaultCoapDeviceTypeConfiguration.getTransportPayloadTypeConfiguration(); - if (transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration) { - ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; - validateProtoSchemas(protoTransportPayloadConfiguration); - validateTelemetryDynamicMessageFields(protoTransportPayloadConfiguration); - validateRpcRequestDynamicMessageFields(protoTransportPayloadConfiguration); - } - } - } else if (transportConfiguration instanceof Lwm2mDeviceProfileTransportConfiguration) { - List lwM2MBootstrapServersConfigurations = ((Lwm2mDeviceProfileTransportConfiguration) transportConfiguration).getBootstrap(); - if (lwM2MBootstrapServersConfigurations != null) { - validateLwm2mServersConfigOfBootstrapForClient(lwM2MBootstrapServersConfigurations, - ((Lwm2mDeviceProfileTransportConfiguration) transportConfiguration).isBootstrapServerUpdateEnable()); - for (LwM2MBootstrapServerCredential bootstrapServerCredential : lwM2MBootstrapServersConfigurations) { - validateLwm2mServersCredentialOfBootstrapForClient(bootstrapServerCredential); - } - } - } - - List profileAlarms = deviceProfile.getProfileData().getAlarms(); - - if (!CollectionUtils.isEmpty(profileAlarms)) { - Set alarmTypes = new HashSet<>(); - for (DeviceProfileAlarm alarm : profileAlarms) { - String alarmType = alarm.getAlarmType(); - if (StringUtils.isEmpty(alarmType)) { - throw new DataValidationException("Alarm rule type should be specified!"); - } - if (!alarmTypes.add(alarmType)) { - throw new DataValidationException(String.format("Can't create device profile with the same alarm rule types: \"%s\"!", alarmType)); - } - } - } - - if (deviceProfile.getDefaultRuleChainId() != null) { - RuleChain ruleChain = ruleChainService.findRuleChainById(tenantId, deviceProfile.getDefaultRuleChainId()); - if (ruleChain == null) { - throw new DataValidationException("Can't assign non-existent rule chain!"); - } - } - - if (deviceProfile.getDefaultDashboardId() != null) { - DashboardInfo dashboard = dashboardService.findDashboardInfoById(tenantId, deviceProfile.getDefaultDashboardId()); - if (dashboard == null) { - throw new DataValidationException("Can't assign non-existent dashboard!"); - } - } - - if (deviceProfile.getFirmwareId() != null) { - OtaPackage firmware = otaPackageService.findOtaPackageById(tenantId, deviceProfile.getFirmwareId()); - if (firmware == null) { - throw new DataValidationException("Can't assign non-existent firmware!"); - } - if (!firmware.getType().equals(OtaPackageType.FIRMWARE)) { - throw new DataValidationException("Can't assign firmware with type: " + firmware.getType()); - } - if (firmware.getData() == null && !firmware.hasUrl()) { - throw new DataValidationException("Can't assign firmware with empty data!"); - } - if (!firmware.getDeviceProfileId().equals(deviceProfile.getId())) { - throw new DataValidationException("Can't assign firmware with different deviceProfile!"); - } - } - - if (deviceProfile.getSoftwareId() != null) { - OtaPackage software = otaPackageService.findOtaPackageById(tenantId, deviceProfile.getSoftwareId()); - if (software == null) { - throw new DataValidationException("Can't assign non-existent software!"); - } - if (!software.getType().equals(OtaPackageType.SOFTWARE)) { - throw new DataValidationException("Can't assign software with type: " + software.getType()); - } - if (software.getData() == null && !software.hasUrl()) { - throw new DataValidationException("Can't assign software with empty data!"); - } - if (!software.getDeviceProfileId().equals(deviceProfile.getId())) { - throw new DataValidationException("Can't assign firmware with different deviceProfile!"); - } - } - } - - @Override - protected void validateUpdate(TenantId tenantId, DeviceProfile deviceProfile) { - DeviceProfile old = deviceProfileDao.findById(deviceProfile.getTenantId(), deviceProfile.getId().getId()); - if (old == null) { - throw new DataValidationException("Can't update non existing device profile!"); - } - boolean profileTypeChanged = !old.getType().equals(deviceProfile.getType()); - boolean transportTypeChanged = !old.getTransportType().equals(deviceProfile.getTransportType()); - if (profileTypeChanged || transportTypeChanged) { - Long profileDeviceCount = deviceDao.countDevicesByDeviceProfileId(deviceProfile.getTenantId(), deviceProfile.getId().getId()); - if (profileDeviceCount > 0) { - String message = null; - if (profileTypeChanged) { - message = "Can't change device profile type because devices referenced it!"; - } else if (transportTypeChanged) { - message = "Can't change device profile transport type because devices referenced it!"; - } - throw new DataValidationException(message); - } - } - } - - private void validateProtoSchemas(ProtoTransportPayloadConfiguration protoTransportPayloadTypeConfiguration) { - try { - validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceAttributesProtoSchema(), ATTRIBUTES_PROTO_SCHEMA); - validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceTelemetryProtoSchema(), TELEMETRY_PROTO_SCHEMA); - validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceRpcRequestProtoSchema(), RPC_REQUEST_PROTO_SCHEMA); - validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceRpcResponseProtoSchema(), RPC_RESPONSE_PROTO_SCHEMA); - } catch (Exception exception) { - throw new DataValidationException(exception.getMessage()); - } - } - - private void validateTransportProtoSchema(String schema, String schemaName) throws IllegalArgumentException { - ProtoParser schemaParser = new ProtoParser(LOCATION, schema.toCharArray()); - ProtoFileElement protoFileElement; - try { - protoFileElement = schemaParser.readProtoFile(); - } catch (Exception e) { - throw new IllegalArgumentException("[Transport Configuration] failed to parse " + schemaName + " due to: " + e.getMessage()); - } - checkProtoFileSyntax(schemaName, protoFileElement); - checkProtoFileCommonSettings(schemaName, protoFileElement.getOptions().isEmpty(), " Schema options don't support!"); - checkProtoFileCommonSettings(schemaName, protoFileElement.getPublicImports().isEmpty(), " Schema public imports don't support!"); - checkProtoFileCommonSettings(schemaName, protoFileElement.getImports().isEmpty(), " Schema imports don't support!"); - checkProtoFileCommonSettings(schemaName, protoFileElement.getExtendDeclarations().isEmpty(), " Schema extend declarations don't support!"); - checkTypeElements(schemaName, protoFileElement); - } - - private void checkProtoFileSyntax(String schemaName, ProtoFileElement protoFileElement) { - if (protoFileElement.getSyntax() == null || !protoFileElement.getSyntax().equals(Syntax.PROTO_3)) { - throw new IllegalArgumentException("[Transport Configuration] invalid schema syntax: " + protoFileElement.getSyntax() + - " for " + schemaName + " provided! Only " + Syntax.PROTO_3 + " allowed!"); - } - } - - private void checkProtoFileCommonSettings(String schemaName, boolean isEmptySettings, String invalidSettingsMessage) { - if (!isEmptySettings) { - throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + invalidSettingsMessage); - } - } - - private void checkTypeElements(String schemaName, ProtoFileElement protoFileElement) { - List types = protoFileElement.getTypes(); - if (!types.isEmpty()) { - if (types.stream().noneMatch(typeElement -> typeElement instanceof MessageElement)) { - throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " At least one Message definition should exists!"); - } else { - checkEnumElements(schemaName, getEnumElements(types)); - checkMessageElements(schemaName, getMessageTypes(types)); - } - } else { - throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Type elements is empty!"); - } - } - - private void checkFieldElements(String schemaName, List fieldElements) { - if (!fieldElements.isEmpty()) { - boolean hasRequiredLabel = fieldElements.stream().anyMatch(fieldElement -> { - Field.Label label = fieldElement.getLabel(); - return label != null && label.equals(Field.Label.REQUIRED); - }); - if (hasRequiredLabel) { - throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Required labels are not supported!"); - } - boolean hasDefaultValue = fieldElements.stream().anyMatch(fieldElement -> fieldElement.getDefaultValue() != null); - if (hasDefaultValue) { - throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Default values are not supported!"); - } - } - } - - private void checkEnumElements(String schemaName, List enumTypes) { - if (enumTypes.stream().anyMatch(enumElement -> !enumElement.getNestedTypes().isEmpty())) { - throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Nested types in Enum definitions are not supported!"); - } - if (enumTypes.stream().anyMatch(enumElement -> !enumElement.getOptions().isEmpty())) { - throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Enum definitions options are not supported!"); - } - } - - private void checkMessageElements(String schemaName, List messageElementsList) { - if (!messageElementsList.isEmpty()) { - messageElementsList.forEach(messageElement -> { - checkProtoFileCommonSettings(schemaName, messageElement.getGroups().isEmpty(), - " Message definition groups don't support!"); - checkProtoFileCommonSettings(schemaName, messageElement.getOptions().isEmpty(), - " Message definition options don't support!"); - checkProtoFileCommonSettings(schemaName, messageElement.getExtensions().isEmpty(), - " Message definition extensions don't support!"); - checkProtoFileCommonSettings(schemaName, messageElement.getReserveds().isEmpty(), - " Message definition reserved elements don't support!"); - checkFieldElements(schemaName, messageElement.getFields()); - List oneOfs = messageElement.getOneOfs(); - if (!oneOfs.isEmpty()) { - oneOfs.forEach(oneOfElement -> { - checkProtoFileCommonSettings(schemaName, oneOfElement.getGroups().isEmpty(), - " OneOf definition groups don't support!"); - checkFieldElements(schemaName, oneOfElement.getFields()); - }); - } - List nestedTypes = messageElement.getNestedTypes(); - if (!nestedTypes.isEmpty()) { - List nestedEnumTypes = getEnumElements(nestedTypes); - if (!nestedEnumTypes.isEmpty()) { - checkEnumElements(schemaName, nestedEnumTypes); - } - List nestedMessageTypes = getMessageTypes(nestedTypes); - checkMessageElements(schemaName, nestedMessageTypes); - } - }); - } - } - - private List getMessageTypes(List types) { - return types.stream() - .filter(typeElement -> typeElement instanceof MessageElement) - .map(typeElement -> (MessageElement) typeElement) - .collect(Collectors.toList()); - } - - private List getEnumElements(List types) { - return types.stream() - .filter(typeElement -> typeElement instanceof EnumElement) - .map(typeElement -> (EnumElement) typeElement) - .collect(Collectors.toList()); - } - - }; - - private void validateTelemetryDynamicMessageFields(ProtoTransportPayloadConfiguration protoTransportPayloadTypeConfiguration) { - String deviceTelemetryProtoSchema = protoTransportPayloadTypeConfiguration.getDeviceTelemetryProtoSchema(); - Descriptors.Descriptor telemetryDynamicMessageDescriptor = protoTransportPayloadTypeConfiguration.getTelemetryDynamicMessageDescriptor(deviceTelemetryProtoSchema); - if (telemetryDynamicMessageDescriptor == null) { - throw new DataValidationException(invalidSchemaProvidedMessage(TELEMETRY_PROTO_SCHEMA) + " Failed to get telemetryDynamicMessageDescriptor!"); - } else { - List fields = telemetryDynamicMessageDescriptor.getFields(); - if (CollectionUtils.isEmpty(fields)) { - throw new DataValidationException(invalidSchemaProvidedMessage(TELEMETRY_PROTO_SCHEMA) + " " + telemetryDynamicMessageDescriptor.getName() + " fields is empty!"); - } else if (fields.size() == 2) { - Descriptors.FieldDescriptor tsFieldDescriptor = telemetryDynamicMessageDescriptor.findFieldByName("ts"); - Descriptors.FieldDescriptor valuesFieldDescriptor = telemetryDynamicMessageDescriptor.findFieldByName("values"); - if (tsFieldDescriptor != null && valuesFieldDescriptor != null) { - if (!Descriptors.FieldDescriptor.Type.MESSAGE.equals(valuesFieldDescriptor.getType())) { - throw new DataValidationException(invalidSchemaProvidedMessage(TELEMETRY_PROTO_SCHEMA) + " Field 'values' has invalid data type. Only message type is supported!"); - } - if (!Descriptors.FieldDescriptor.Type.INT64.equals(tsFieldDescriptor.getType())) { - throw new DataValidationException(invalidSchemaProvidedMessage(TELEMETRY_PROTO_SCHEMA) + " Field 'ts' has invalid data type. Only int64 type is supported!"); - } - if (!tsFieldDescriptor.hasOptionalKeyword()) { - throw new DataValidationException(invalidSchemaProvidedMessage(TELEMETRY_PROTO_SCHEMA) + " Field 'ts' has invalid label. Field 'ts' should have optional keyword!"); - } - } - } - } - } - - private void validateRpcRequestDynamicMessageFields(ProtoTransportPayloadConfiguration protoTransportPayloadTypeConfiguration) { - DynamicMessage.Builder rpcRequestDynamicMessageBuilder = protoTransportPayloadTypeConfiguration.getRpcRequestDynamicMessageBuilder(protoTransportPayloadTypeConfiguration.getDeviceRpcRequestProtoSchema()); - Descriptors.Descriptor rpcRequestDynamicMessageDescriptor = rpcRequestDynamicMessageBuilder.getDescriptorForType(); - if (rpcRequestDynamicMessageDescriptor == null) { - throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Failed to get rpcRequestDynamicMessageDescriptor!"); - } else { - if (CollectionUtils.isEmpty(rpcRequestDynamicMessageDescriptor.getFields()) || rpcRequestDynamicMessageDescriptor.getFields().size() != 3) { - throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " " + rpcRequestDynamicMessageDescriptor.getName() + " message should always contains 3 fields: method, requestId and params!"); - } - Descriptors.FieldDescriptor methodFieldDescriptor = rpcRequestDynamicMessageDescriptor.findFieldByName("method"); - if (methodFieldDescriptor == null) { - throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Failed to get field descriptor for field: method!"); - } else { - if (!Descriptors.FieldDescriptor.Type.STRING.equals(methodFieldDescriptor.getType())) { - throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Field 'method' has invalid data type. Only string type is supported!"); - } - if (methodFieldDescriptor.isRepeated()) { - throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Field 'method' has invalid label!"); - } - } - Descriptors.FieldDescriptor requestIdFieldDescriptor = rpcRequestDynamicMessageDescriptor.findFieldByName("requestId"); - if (requestIdFieldDescriptor == null) { - throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Failed to get field descriptor for field: requestId!"); - } else { - if (!Descriptors.FieldDescriptor.Type.INT32.equals(requestIdFieldDescriptor.getType())) { - throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Field 'requestId' has invalid data type. Only int32 type is supported!"); - } - if (requestIdFieldDescriptor.isRepeated()) { - throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Field 'requestId' has invalid label!"); - } - } - Descriptors.FieldDescriptor paramsFieldDescriptor = rpcRequestDynamicMessageDescriptor.findFieldByName("params"); - if (paramsFieldDescriptor == null) { - throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Failed to get field descriptor for field: params!"); - } else { - if (paramsFieldDescriptor.isRepeated()) { - throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Field 'params' has invalid label!"); - } - } - } - } - - private void validateLwm2mServersConfigOfBootstrapForClient(List lwM2MBootstrapServersConfigurations, boolean isBootstrapServerUpdateEnable) { - Set uris = new HashSet<>(); - Set shortServerIds = new HashSet<>(); - for (LwM2MBootstrapServerCredential bootstrapServerCredential : lwM2MBootstrapServersConfigurations) { - AbstractLwM2MBootstrapServerCredential serverConfig = (AbstractLwM2MBootstrapServerCredential) bootstrapServerCredential; - if (!isBootstrapServerUpdateEnable && serverConfig.isBootstrapServerIs()) { - throw new DeviceCredentialsValidationException("Bootstrap config must not include \"Bootstrap Server\". \"Include Bootstrap Server updates\" is " + isBootstrapServerUpdateEnable + "."); - } - String server = serverConfig.isBootstrapServerIs() ? "Bootstrap Server" : "LwM2M Server" + " shortServerId: " + serverConfig.getShortServerId() + ":"; - if (serverConfig.getShortServerId() < 1 || serverConfig.getShortServerId() > 65534) { - throw new DeviceCredentialsValidationException(server + " ShortServerId must not be less than 1 and more than 65534!"); - } - if (!shortServerIds.add(serverConfig.getShortServerId())) { - throw new DeviceCredentialsValidationException(server + " \"Short server Id\" value = " + serverConfig.getShortServerId() + ". This value must be a unique value for all servers!"); - } - String uri = serverConfig.getHost() + ":" + serverConfig.getPort(); - if (!uris.add(uri)) { - throw new DeviceCredentialsValidationException(server + " \"Host + port\" value = " + uri + ". This value must be a unique value for all servers!"); - } - Integer port; - if (LwM2MSecurityMode.NO_SEC.equals(serverConfig.getSecurityMode())) { - port = serverConfig.isBootstrapServerIs() ? 5687 : 5685; - } else { - port = serverConfig.isBootstrapServerIs() ? 5688 : 5686; - } - if (serverConfig.getPort() == null || serverConfig.getPort().intValue() != port) { - throw new DeviceCredentialsValidationException(server + " \"Port\" value = " + serverConfig.getPort() + ". This value for security " + serverConfig.getSecurityMode().name() + " must be " + port + "!"); - } - } - } - - private void validateLwm2mServersCredentialOfBootstrapForClient(LwM2MBootstrapServerCredential bootstrapServerConfig) { - String server; - switch (bootstrapServerConfig.getSecurityMode()) { - case NO_SEC: - case PSK: - break; - case RPK: - RPKLwM2MBootstrapServerCredential rpkServerCredentials = (RPKLwM2MBootstrapServerCredential) bootstrapServerConfig; - server = rpkServerCredentials.isBootstrapServerIs() ? "Bootstrap Server" : "LwM2M Server"; - if (StringUtils.isEmpty(rpkServerCredentials.getServerPublicKey())) { - throw new DeviceCredentialsValidationException(server + " RPK public key must be specified!"); - } - try { - String pubkRpkSever = EncryptionUtil.pubkTrimNewLines(rpkServerCredentials.getServerPublicKey()); - rpkServerCredentials.setServerPublicKey(pubkRpkSever); - SecurityUtil.publicKey.decode(rpkServerCredentials.getDecodedCServerPublicKey()); - } catch (Exception e) { - throw new DeviceCredentialsValidationException(server + " RPK public key must be in standard [RFC7250] and then encoded to Base64 format!"); - } - break; - case X509: - X509LwM2MBootstrapServerCredential x509ServerCredentials = (X509LwM2MBootstrapServerCredential) bootstrapServerConfig; - server = x509ServerCredentials.isBootstrapServerIs() ? "Bootstrap Server" : "LwM2M Server"; - if (StringUtils.isEmpty(x509ServerCredentials.getServerPublicKey())) { - throw new DeviceCredentialsValidationException(server + " X509 certificate must be specified!"); - } - - try { - String certServer = EncryptionUtil.certTrimNewLines(x509ServerCredentials.getServerPublicKey()); - x509ServerCredentials.setServerPublicKey(certServer); - SecurityUtil.certificate.decode(x509ServerCredentials.getDecodedCServerPublicKey()); - } catch (Exception e) { - throw new DeviceCredentialsValidationException(server + " X509 certificate must be in DER-encoded X509v3 format and support only EC algorithm and then encoded to Base64 format!"); - } - break; - } - } - private PaginatedRemover tenantDeviceProfilesRemover = new PaginatedRemover() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index b2a6c10337..a3260ffa17 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -23,18 +23,14 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomStringUtils; import org.hibernate.exception.ConstraintViolationException; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Caching; -import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceInfo; import org.thingsboard.server.common.data.DeviceProfile; @@ -42,15 +38,12 @@ import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; -import org.thingsboard.server.common.data.OtaPackage; -import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.device.DeviceSearchQuery; import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; import org.thingsboard.server.common.data.device.data.CoapDeviceTransportConfiguration; import org.thingsboard.server.common.data.device.data.DefaultDeviceConfiguration; import org.thingsboard.server.common.data.device.data.DefaultDeviceTransportConfiguration; import org.thingsboard.server.common.data.device.data.DeviceData; -import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration; import org.thingsboard.server.common.data.device.data.Lwm2mDeviceTransportConfiguration; import org.thingsboard.server.common.data.device.data.MqttDeviceTransportConfiguration; import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration; @@ -69,23 +62,18 @@ import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; -import org.thingsboard.server.dao.customer.CustomerDao; +import org.thingsboard.server.dao.cache.EntitiesCacheManager; import org.thingsboard.server.dao.device.provision.ProvisionFailedException; import org.thingsboard.server.dao.device.provision.ProvisionRequest; import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; -import org.thingsboard.server.dao.tenant.TbTenantProfileCache; -import org.thingsboard.server.dao.tenant.TenantDao; import javax.annotation.Nullable; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -96,7 +84,6 @@ import java.util.stream.Collectors; import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CACHE; import static org.thingsboard.server.dao.DaoUtil.toUUIDs; -import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validateIds; import static org.thingsboard.server.dao.service.Validator.validatePageLink; @@ -116,12 +103,6 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @Autowired private DeviceDao deviceDao; - @Autowired - private TenantDao tenantDao; - - @Autowired - private CustomerDao customerDao; - @Autowired private DeviceCredentialsService deviceCredentialsService; @@ -129,17 +110,13 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe private DeviceProfileService deviceProfileService; @Autowired - private CacheManager cacheManager; + private EntitiesCacheManager cacheManager; @Autowired private EventService eventService; @Autowired - @Lazy - private TbTenantProfileCache tenantProfileCache; - - @Autowired - private OtaPackageService otaPackageService; + private DataValidator deviceValidator; @Override public DeviceInfo findDeviceInfoById(TenantId tenantId, DeviceId deviceId) { @@ -274,8 +251,8 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_name_unq_key")) { // remove device from cache in case null value cached in the distributed redis. - removeDeviceFromCacheByName(device.getTenantId(), device.getName()); - removeDeviceFromCacheById(device.getTenantId(), device.getId()); + cacheManager.removeDeviceFromCacheByName(device.getTenantId(), device.getName()); + cacheManager.removeDeviceFromCacheById(device.getTenantId(), device.getId()); throw new DataValidationException("Device with such name already exists!"); } else { throw t; @@ -321,8 +298,8 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe Device device = findDeviceById(tenantId, deviceId); device.setCustomerId(customerId); Device savedDevice = saveDevice(device); - removeDeviceFromCacheByName(tenantId, device.getName()); - removeDeviceFromCacheById(tenantId, device.getId()); + cacheManager.removeDeviceFromCacheByName(tenantId, device.getName()); + cacheManager.removeDeviceFromCacheById(tenantId, device.getId()); return savedDevice; } @@ -331,8 +308,8 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe Device device = findDeviceById(tenantId, deviceId); device.setCustomerId(null); Device savedDevice = saveDevice(device); - removeDeviceFromCacheByName(tenantId, device.getName()); - removeDeviceFromCacheById(tenantId, device.getId()); + cacheManager.removeDeviceFromCacheByName(tenantId, device.getName()); + cacheManager.removeDeviceFromCacheById(tenantId, device.getId()); return savedDevice; } @@ -362,22 +339,11 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe deviceDao.removeById(tenantId, deviceId.getId()); - removeDeviceFromCacheByName(tenantId, deviceName); - removeDeviceFromCacheById(tenantId, deviceId); + cacheManager.removeDeviceFromCacheByName(tenantId, deviceName); + cacheManager.removeDeviceFromCacheById(tenantId, deviceId); } - private void removeDeviceFromCacheByName(TenantId tenantId, String name) { - Cache cache = cacheManager.getCache(DEVICE_CACHE); - cache.evict(Arrays.asList(tenantId, name)); - } - private void removeDeviceFromCacheById(TenantId tenantId, DeviceId deviceId) { - if (deviceId == null) { - return; - } - Cache cache = cacheManager.getCache(DEVICE_CACHE); - cache.evict(Arrays.asList(tenantId, deviceId)); - } @Override public PageData findDevicesByTenantId(TenantId tenantId, PageLink pageLink) { @@ -590,8 +556,8 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe // explicitly remove device with previous tenant id from cache // result device object will have different tenant id and will not remove entity from cache - removeDeviceFromCacheByName(oldTenantId, device.getName()); - removeDeviceFromCacheById(oldTenantId, device.getId()); + cacheManager.removeDeviceFromCacheByName(oldTenantId, device.getName()); + cacheManager.removeDeviceFromCacheById(oldTenantId, device.getId()); return savedDevice; } @@ -639,7 +605,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); } } - removeDeviceFromCacheById(savedDevice.getTenantId(), savedDevice.getId()); // eviction by name is described as annotation @CacheEvict above + cacheManager.removeDeviceFromCacheById(savedDevice.getTenantId(), savedDevice.getId()); // eviction by name is described as annotation @CacheEvict above return savedDevice; } @@ -710,91 +676,6 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe return deviceDao.countByTenantId(tenantId); } - private DataValidator deviceValidator = - new DataValidator() { - - @Override - protected void validateCreate(TenantId tenantId, Device device) { - DefaultTenantProfileConfiguration profileConfiguration = - (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); - long maxDevices = profileConfiguration.getMaxDevices(); - validateNumberOfEntitiesPerTenant(tenantId, deviceDao, maxDevices, EntityType.DEVICE); - } - - @Override - protected void validateUpdate(TenantId tenantId, Device device) { - Device old = deviceDao.findById(device.getTenantId(), device.getId().getId()); - if (old == null) { - throw new DataValidationException("Can't update non existing device!"); - } - if (!old.getName().equals(device.getName())) { - removeDeviceFromCacheByName(tenantId, old.getName()); - removeDeviceFromCacheById(tenantId, device.getId()); - } - } - - @Override - protected void validateDataImpl(TenantId tenantId, Device device) { - if (StringUtils.isEmpty(device.getName()) || device.getName().trim().length() == 0) { - throw new DataValidationException("Device name should be specified!"); - } - if (device.getTenantId() == null) { - throw new DataValidationException("Device should be assigned to tenant!"); - } else { - Tenant tenant = tenantDao.findById(device.getTenantId(), device.getTenantId().getId()); - if (tenant == null) { - throw new DataValidationException("Device is referencing to non-existent tenant!"); - } - } - if (device.getCustomerId() == null) { - device.setCustomerId(new CustomerId(NULL_UUID)); - } else if (!device.getCustomerId().getId().equals(NULL_UUID)) { - Customer customer = customerDao.findById(device.getTenantId(), device.getCustomerId().getId()); - if (customer == null) { - throw new DataValidationException("Can't assign device to non-existent customer!"); - } - if (!customer.getTenantId().getId().equals(device.getTenantId().getId())) { - throw new DataValidationException("Can't assign device to customer from different tenant!"); - } - } - Optional.ofNullable(device.getDeviceData()) - .flatMap(deviceData -> Optional.ofNullable(deviceData.getTransportConfiguration())) - .ifPresent(DeviceTransportConfiguration::validate); - - if (device.getFirmwareId() != null) { - OtaPackage firmware = otaPackageService.findOtaPackageById(tenantId, device.getFirmwareId()); - if (firmware == null) { - throw new DataValidationException("Can't assign non-existent firmware!"); - } - if (!firmware.getType().equals(OtaPackageType.FIRMWARE)) { - throw new DataValidationException("Can't assign firmware with type: " + firmware.getType()); - } - if (firmware.getData() == null && !firmware.hasUrl()) { - throw new DataValidationException("Can't assign firmware with empty data!"); - } - if (!firmware.getDeviceProfileId().equals(device.getDeviceProfileId())) { - throw new DataValidationException("Can't assign firmware with different deviceProfile!"); - } - } - - if (device.getSoftwareId() != null) { - OtaPackage software = otaPackageService.findOtaPackageById(tenantId, device.getSoftwareId()); - if (software == null) { - throw new DataValidationException("Can't assign non-existent software!"); - } - if (!software.getType().equals(OtaPackageType.SOFTWARE)) { - throw new DataValidationException("Can't assign software with type: " + software.getType()); - } - if (software.getData() == null && !software.hasUrl()) { - throw new DataValidationException("Can't assign software with empty data!"); - } - if (!software.getDeviceProfileId().equals(device.getDeviceProfileId())) { - throw new DataValidationException("Can't assign firmware with different deviceProfile!"); - } - } - } - }; - private PaginatedRemover tenantDevicesRemover = new PaginatedRemover() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java b/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java index aa96850a67..9d18146ec2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java @@ -23,7 +23,6 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.TimePageLink; -import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; @Service @@ -33,6 +32,9 @@ public class BaseEdgeEventService implements EdgeEventService { @Autowired private EdgeEventDao edgeEventDao; + @Autowired + private DataValidator edgeEventValidator; + @Override public EdgeEvent save(EdgeEvent edgeEvent) { edgeEventValidator.validate(edgeEvent, EdgeEvent::getTenantId); @@ -48,17 +50,4 @@ public class BaseEdgeEventService implements EdgeEventService { public void cleanupEvents(long ttl) { edgeEventDao.cleanupEvents(ttl); } - - private DataValidator edgeEventValidator = - new DataValidator() { - @Override - protected void validateDataImpl(TenantId tenantId, EdgeEvent edgeEvent) { - if (edgeEvent.getEdgeId() == null) { - throw new DataValidationException("Edge id should be specified!"); - } - if (edgeEvent.getAction() == null) { - throw new DataValidationException("Edge Event action should be specified!"); - } - } - }; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java index 3cdbcf7bc9..80d3a1bb03 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java @@ -25,16 +25,11 @@ import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; import org.hibernate.exception.ConstraintViolationException; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; -import org.springframework.util.StringUtils; -import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeInfo; @@ -52,7 +47,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; -import org.thingsboard.server.dao.customer.CustomerDao; +import org.thingsboard.server.dao.cache.EntitiesCacheManager; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.relation.RelationService; @@ -60,12 +55,10 @@ import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; -import org.thingsboard.server.dao.tenant.TenantDao; import org.thingsboard.server.dao.user.UserService; import javax.annotation.Nullable; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -74,7 +67,6 @@ import java.util.stream.Collectors; import static org.thingsboard.server.common.data.CacheConstants.EDGE_CACHE; import static org.thingsboard.server.dao.DaoUtil.toUUIDs; -import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validateIds; import static org.thingsboard.server.dao.service.Validator.validatePageLink; @@ -95,17 +87,11 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic @Autowired private EdgeDao edgeDao; - @Autowired - private TenantDao tenantDao; - - @Autowired - private CustomerDao customerDao; - @Autowired private UserService userService; @Autowired - private CacheManager cacheManager; + private EntitiesCacheManager cacheManager; @Autowired private RuleChainService ruleChainService; @@ -113,6 +99,9 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic @Autowired private RelationService relationService; + @Autowired + private DataValidator edgeValidator; + @Override public Edge findEdgeById(TenantId tenantId, EdgeId edgeId) { log.trace("Executing findEdgeById [{}]", edgeId); @@ -152,11 +141,9 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic @CacheEvict(cacheNames = EDGE_CACHE, key = "{#edge.tenantId, #edge.name}") @Override - public Edge saveEdge(Edge edge, boolean doValidate) { + public Edge saveEdge(Edge edge) { log.trace("Executing saveEdge [{}]", edge); - if (doValidate) { - edgeValidator.validate(edge, Edge::getTenantId); - } + edgeValidator.validate(edge, Edge::getTenantId); try { return edgeDao.save(edge.getTenantId(), edge); } catch (Exception t) { @@ -175,7 +162,7 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic log.trace("[{}] Executing assignEdgeToCustomer [{}][{}]", tenantId, edgeId, customerId); Edge edge = findEdgeById(tenantId, edgeId); edge.setCustomerId(customerId); - return saveEdge(edge, true); + return saveEdge(edge); } @Override @@ -183,7 +170,7 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic log.trace("[{}] Executing unassignEdgeFromCustomer [{}]", tenantId, edgeId); Edge edge = findEdgeById(tenantId, edgeId); edge.setCustomerId(null); - return saveEdge(edge, true); + return saveEdge(edge); } @Override @@ -195,16 +182,11 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic deleteEntityRelations(tenantId, edgeId); - removeEdgeFromCacheByName(edge.getTenantId(), edge.getName()); + cacheManager.removeEdgeFromCacheByName(edge.getTenantId(), edge.getName()); edgeDao.removeById(tenantId, edgeId.getId()); } - private void removeEdgeFromCacheByName(TenantId tenantId, String name) { - Cache cache = cacheManager.getCache(EDGE_CACHE); - cache.evict(Arrays.asList(tenantId, name)); - } - @Override public PageData findEdgesByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findEdgesByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); @@ -330,7 +312,9 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic @Nullable @Override public List apply(@Nullable List edgeList) { - return edgeList == null ? Collections.emptyList() : edgeList.stream().filter(edge -> query.getEdgeTypes().contains(edge.getType())).collect(Collectors.toList()); + return edgeList == null ? + Collections.emptyList() : + edgeList.stream().filter(edge -> query.getEdgeTypes().contains(edge.getType())).collect(Collectors.toList()); } }, MoreExecutors.directExecutor()); @@ -375,57 +359,6 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic return edgeDao.findEdgesByTenantIdAndEntityId(tenantId.getId(), entityId.getId(), entityId.getEntityType(), pageLink); } - private DataValidator edgeValidator = - new DataValidator() { - - @Override - protected void validateCreate(TenantId tenantId, Edge edge) { - } - - @Override - protected void validateUpdate(TenantId tenantId, Edge edge) { - Edge old = edgeDao.findById(edge.getTenantId(), edge.getId().getId()); - if (!old.getName().equals(edge.getName())) { - removeEdgeFromCacheByName(tenantId, old.getName()); - } - } - - @Override - protected void validateDataImpl(TenantId tenantId, Edge edge) { - if (StringUtils.isEmpty(edge.getType())) { - throw new DataValidationException("Edge type should be specified!"); - } - if (StringUtils.isEmpty(edge.getName())) { - throw new DataValidationException("Edge name should be specified!"); - } - if (StringUtils.isEmpty(edge.getSecret())) { - throw new DataValidationException("Edge secret should be specified!"); - } - if (StringUtils.isEmpty(edge.getRoutingKey())) { - throw new DataValidationException("Edge routing key should be specified!"); - } - if (edge.getTenantId() == null) { - throw new DataValidationException("Edge should be assigned to tenant!"); - } else { - Tenant tenant = tenantDao.findById(edge.getTenantId(), edge.getTenantId().getId()); - if (tenant == null) { - throw new DataValidationException("Edge is referencing to non-existent tenant!"); - } - } - if (edge.getCustomerId() == null) { - edge.setCustomerId(new CustomerId(NULL_UUID)); - } else if (!edge.getCustomerId().getId().equals(NULL_UUID)) { - Customer customer = customerDao.findById(edge.getTenantId(), edge.getCustomerId().getId()); - if (customer == null) { - throw new DataValidationException("Can't assign edge to non-existent customer!"); - } - if (!customer.getTenantId().getId().equals(edge.getTenantId().getId())) { - throw new DataValidationException("Can't assign edge to customer from different tenant!"); - } - } - } - }; - private PaginatedRemover tenantEdgesRemover = new PaginatedRemover() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java index 624c5e5ef7..e724980123 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java @@ -21,7 +21,6 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; @@ -29,12 +28,10 @@ import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Caching; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityViewInfo; -import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; import org.thingsboard.server.common.data.id.CustomerId; @@ -47,12 +44,10 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.RelationTypeGroup; -import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; -import org.thingsboard.server.dao.tenant.TenantDao; import javax.annotation.Nullable; import java.util.ArrayList; @@ -65,7 +60,6 @@ import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import static org.thingsboard.server.common.data.CacheConstants.ENTITY_VIEW_CACHE; -import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validatePageLink; import static org.thingsboard.server.dao.service.Validator.validateString; @@ -87,13 +81,10 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti private EntityViewDao entityViewDao; @Autowired - private TenantDao tenantDao; - - @Autowired - private CustomerDao customerDao; + private CacheManager cacheManager; @Autowired - private CacheManager cacheManager; + private DataValidator entityViewValidator; @Caching(evict = { @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityView.tenantId, #entityView.entityId}"), @@ -103,8 +94,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti public EntityView saveEntityView(EntityView entityView) { log.trace("Executing save entity view [{}]", entityView); entityViewValidator.validate(entityView, EntityView::getTenantId); - EntityView savedEntityView = entityViewDao.save(entityView.getTenantId(), entityView); - return savedEntityView; + return entityViewDao.save(entityView.getTenantId(), entityView); } @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}") @@ -400,57 +390,6 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti return entityViewDao.findEntityViewsByTenantIdAndEdgeIdAndType(tenantId.getId(), edgeId.getId(), type, pageLink); } - private DataValidator entityViewValidator = - new DataValidator() { - - @Override - protected void validateCreate(TenantId tenantId, EntityView entityView) { - entityViewDao.findEntityViewByTenantIdAndName(entityView.getTenantId().getId(), entityView.getName()) - .ifPresent(e -> { - throw new DataValidationException("Entity view with such name already exists!"); - }); - } - - @Override - protected void validateUpdate(TenantId tenantId, EntityView entityView) { - entityViewDao.findEntityViewByTenantIdAndName(entityView.getTenantId().getId(), entityView.getName()) - .ifPresent(e -> { - if (!e.getUuidId().equals(entityView.getUuidId())) { - throw new DataValidationException("Entity view with such name already exists!"); - } - }); - } - - @Override - protected void validateDataImpl(TenantId tenantId, EntityView entityView) { - if (StringUtils.isEmpty(entityView.getType())) { - throw new DataValidationException("Entity View type should be specified!"); - } - if (StringUtils.isEmpty(entityView.getName())) { - throw new DataValidationException("Entity view name should be specified!"); - } - if (entityView.getTenantId() == null) { - throw new DataValidationException("Entity view should be assigned to tenant!"); - } else { - Tenant tenant = tenantDao.findById(tenantId, entityView.getTenantId().getId()); - if (tenant == null) { - throw new DataValidationException("Entity view is referencing to non-existent tenant!"); - } - } - if (entityView.getCustomerId() == null) { - entityView.setCustomerId(new CustomerId(NULL_UUID)); - } else if (!entityView.getCustomerId().getId().equals(NULL_UUID)) { - Customer customer = customerDao.findById(tenantId, entityView.getCustomerId().getId()); - if (customer == null) { - throw new DataValidationException("Can't assign entity view to non-existent customer!"); - } - if (!customer.getTenantId().getId().equals(entityView.getTenantId().getId())) { - throw new DataValidationException("Can't assign entity view to customer from different tenant!"); - } - } - } - }; - private PaginatedRemover tenantEntityViewRemover = new PaginatedRemover() { @Override protected PageData findEntities(TenantId tenantId, TenantId id, PageLink pageLink) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java b/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java index 714eefd0b1..3937bbd9c3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java @@ -46,6 +46,9 @@ public class BaseEventService implements EventService { @Autowired public EventDao eventDao; + @Autowired + private DataValidator eventValidator; + @Override public ListenableFuture saveAsync(Event event) { eventValidator.validate(event, Event::getTenantId); @@ -128,20 +131,4 @@ public class BaseEventService implements EventService { public void cleanupEvents(long regularEventStartTs, long regularEventEndTs, long debugEventStartTs, long debugEventEndTs) { eventDao.cleanupEvents(regularEventStartTs, regularEventEndTs, debugEventStartTs, debugEventEndTs); } - - private DataValidator eventValidator = - new DataValidator() { - @Override - protected void validateDataImpl(TenantId tenantId, Event event) { - if (event.getEntityId() == null) { - throw new DataValidationException("Entity id should be specified!."); - } - if (StringUtils.isEmpty(event.getType())) { - throw new DataValidationException("Event type should be specified!."); - } - if (event.getBody() == null) { - throw new DataValidationException("Event body should be specified!."); - } - } - }; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java index a3d6099c5d..d35d7e241c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java @@ -15,15 +15,13 @@ */ package org.thingsboard.server.dao.oauth2; +import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.hibernate.exception.ConstraintViolationException; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.springframework.util.StringUtils; -import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.oauth2.*; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; @@ -36,12 +34,14 @@ import static org.thingsboard.server.dao.service.Validator.validateString; @Slf4j @Service +@AllArgsConstructor public class OAuth2ConfigTemplateServiceImpl extends AbstractEntityService implements OAuth2ConfigTemplateService { - public static final String INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID = "Incorrect clientRegistrationTemplateId "; - public static final String INCORRECT_CLIENT_REGISTRATION_PROVIDER_ID = "Incorrect clientRegistrationProviderId "; - @Autowired - private OAuth2ClientRegistrationTemplateDao clientRegistrationTemplateDao; + private static final String INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID = "Incorrect clientRegistrationTemplateId "; + private static final String INCORRECT_CLIENT_REGISTRATION_PROVIDER_ID = "Incorrect clientRegistrationProviderId "; + + private final OAuth2ClientRegistrationTemplateDao clientRegistrationTemplateDao; + private final DataValidator clientRegistrationTemplateValidator; @Override public OAuth2ClientRegistrationTemplate saveClientRegistrationTemplate(OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { @@ -87,32 +87,4 @@ public class OAuth2ConfigTemplateServiceImpl extends AbstractEntityService imple validateId(templateId, INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID + templateId); clientRegistrationTemplateDao.removeById(TenantId.SYS_TENANT_ID, templateId.getId()); } - - private final DataValidator clientRegistrationTemplateValidator = - new DataValidator() { - - @Override - protected void validateCreate(TenantId tenantId, OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { - } - - @Override - protected void validateUpdate(TenantId tenantId, OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { - } - - @Override - protected void validateDataImpl(TenantId tenantId, OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { - if (StringUtils.isEmpty(clientRegistrationTemplate.getProviderId())) { - throw new DataValidationException("Provider ID should be specified!"); - } - if (clientRegistrationTemplate.getMapperConfig() == null) { - throw new DataValidationException("Mapper config should be specified!"); - } - if (clientRegistrationTemplate.getMapperConfig().getType() == null) { - throw new DataValidationException("Mapper type should be specified!"); - } - if (clientRegistrationTemplate.getMapperConfig().getBasic() == null) { - throw new DataValidationException("Basic mapper config should be specified!"); - } - } - }; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java index 838e79ad70..ecf8d68267 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java @@ -21,18 +21,14 @@ import com.google.common.util.concurrent.ListenableFuture; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.hibernate.exception.ConstraintViolationException; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.Cacheable; -import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.cache.ota.OtaPackageDataCache; -import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.StringUtils; -import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; @@ -40,22 +36,16 @@ import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; -import org.thingsboard.server.dao.device.DeviceProfileDao; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; -import org.thingsboard.server.dao.tenant.TbTenantProfileCache; -import org.thingsboard.server.dao.tenant.TenantDao; import java.nio.ByteBuffer; import java.util.Collections; import java.util.List; -import java.util.Objects; import java.util.Optional; import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_CACHE; -import static org.thingsboard.server.common.data.EntityType.OTA_PACKAGE; import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validatePageLink; @@ -66,16 +56,12 @@ public class BaseOtaPackageService implements OtaPackageService { public static final String INCORRECT_OTA_PACKAGE_ID = "Incorrect otaPackageId "; public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; - private final TenantDao tenantDao; - private final DeviceProfileDao deviceProfileDao; private final OtaPackageDao otaPackageDao; private final OtaPackageInfoDao otaPackageInfoDao; private final CacheManager cacheManager; private final OtaPackageDataCache otaPackageDataCache; - - @Autowired - @Lazy - private TbTenantProfileCache tenantProfileCache; + private final DataValidator otaPackageInfoValidator; + private final DataValidator otaPackageValidator; @Override public OtaPackageInfo saveOtaPackageInfo(OtaPackageInfo otaPackageInfo, boolean isUrl) { @@ -230,168 +216,6 @@ public class BaseOtaPackageService implements OtaPackageService { tenantOtaPackageRemover.removeEntities(tenantId, tenantId); } - private DataValidator otaPackageInfoValidator = new DataValidator<>() { - - @Override - protected void validateDataImpl(TenantId tenantId, OtaPackageInfo otaPackageInfo) { - validateImpl(otaPackageInfo); - } - - @Override - protected void validateUpdate(TenantId tenantId, OtaPackageInfo otaPackage) { - OtaPackageInfo otaPackageOld = otaPackageInfoDao.findById(tenantId, otaPackage.getUuidId()); - BaseOtaPackageService.validateUpdate(otaPackage, otaPackageOld); - } - }; - - private DataValidator otaPackageValidator = new DataValidator<>() { - - @Override - protected void validateCreate(TenantId tenantId, OtaPackage otaPackage) { - DefaultTenantProfileConfiguration profileConfiguration = - (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); - long maxOtaPackagesInBytes = profileConfiguration.getMaxOtaPackagesInBytes(); - validateMaxSumDataSizePerTenant(tenantId, otaPackageDao, maxOtaPackagesInBytes, otaPackage.getDataSize(), OTA_PACKAGE); - } - - @Override - protected void validateDataImpl(TenantId tenantId, OtaPackage otaPackage) { - validateImpl(otaPackage); - - if (!otaPackage.hasUrl()) { - if (StringUtils.isEmpty(otaPackage.getFileName())) { - throw new DataValidationException("OtaPackage file name should be specified!"); - } - - if (StringUtils.isEmpty(otaPackage.getContentType())) { - throw new DataValidationException("OtaPackage content type should be specified!"); - } - - if (otaPackage.getChecksumAlgorithm() == null) { - throw new DataValidationException("OtaPackage checksum algorithm should be specified!"); - } - if (StringUtils.isEmpty(otaPackage.getChecksum())) { - throw new DataValidationException("OtaPackage checksum should be specified!"); - } - - String currentChecksum; - - currentChecksum = generateChecksum(otaPackage.getChecksumAlgorithm(), otaPackage.getData()); - - if (!currentChecksum.equals(otaPackage.getChecksum())) { - throw new DataValidationException("Wrong otaPackage file!"); - } - } else { - if(otaPackage.getData() != null) { - throw new DataValidationException("File can't be saved if URL present!"); - } - } - } - - @Override - protected void validateUpdate(TenantId tenantId, OtaPackage otaPackage) { - OtaPackage otaPackageOld = otaPackageDao.findById(tenantId, otaPackage.getUuidId()); - - BaseOtaPackageService.validateUpdate(otaPackage, otaPackageOld); - - if (otaPackageOld.getData() != null && !otaPackageOld.getData().equals(otaPackage.getData())) { - throw new DataValidationException("Updating otaPackage data is prohibited!"); - } - - if (otaPackageOld.getData() == null && otaPackage.getData() != null) { - DefaultTenantProfileConfiguration profileConfiguration = - (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); - long maxOtaPackagesInBytes = profileConfiguration.getMaxOtaPackagesInBytes(); - validateMaxSumDataSizePerTenant(tenantId, otaPackageDao, maxOtaPackagesInBytes, otaPackage.getDataSize(), OTA_PACKAGE); - } - } - }; - - private static void validateUpdate(OtaPackageInfo otaPackage, OtaPackageInfo otaPackageOld) { - if (!otaPackageOld.getType().equals(otaPackage.getType())) { - throw new DataValidationException("Updating type is prohibited!"); - } - - if (!otaPackageOld.getTitle().equals(otaPackage.getTitle())) { - throw new DataValidationException("Updating otaPackage title is prohibited!"); - } - - if (!otaPackageOld.getVersion().equals(otaPackage.getVersion())) { - throw new DataValidationException("Updating otaPackage version is prohibited!"); - } - - if (!Objects.equals(otaPackage.getTag(), otaPackageOld.getTag())) { - throw new DataValidationException("Updating otaPackage tag is prohibited!"); - } - - if (!otaPackageOld.getDeviceProfileId().equals(otaPackage.getDeviceProfileId())) { - throw new DataValidationException("Updating otaPackage deviceProfile is prohibited!"); - } - - if (otaPackageOld.getFileName() != null && !otaPackageOld.getFileName().equals(otaPackage.getFileName())) { - throw new DataValidationException("Updating otaPackage file name is prohibited!"); - } - - if (otaPackageOld.getContentType() != null && !otaPackageOld.getContentType().equals(otaPackage.getContentType())) { - throw new DataValidationException("Updating otaPackage content type is prohibited!"); - } - - if (otaPackageOld.getChecksumAlgorithm() != null && !otaPackageOld.getChecksumAlgorithm().equals(otaPackage.getChecksumAlgorithm())) { - throw new DataValidationException("Updating otaPackage content type is prohibited!"); - } - - if (otaPackageOld.getChecksum() != null && !otaPackageOld.getChecksum().equals(otaPackage.getChecksum())) { - throw new DataValidationException("Updating otaPackage content type is prohibited!"); - } - - if (otaPackageOld.getDataSize() != null && !otaPackageOld.getDataSize().equals(otaPackage.getDataSize())) { - throw new DataValidationException("Updating otaPackage data size is prohibited!"); - } - - if(otaPackageOld.getUrl() != null && !otaPackageOld.getUrl().equals(otaPackage.getUrl())) { - throw new DataValidationException("Updating otaPackage URL is prohibited!"); - } - } - - private void validateImpl(OtaPackageInfo otaPackageInfo) { - if (otaPackageInfo.getTenantId() == null) { - throw new DataValidationException("OtaPackage should be assigned to tenant!"); - } else { - Tenant tenant = tenantDao.findById(otaPackageInfo.getTenantId(), otaPackageInfo.getTenantId().getId()); - if (tenant == null) { - throw new DataValidationException("OtaPackage is referencing to non-existent tenant!"); - } - } - - if (otaPackageInfo.getDeviceProfileId() != null) { - DeviceProfile deviceProfile = deviceProfileDao.findById(otaPackageInfo.getTenantId(), otaPackageInfo.getDeviceProfileId().getId()); - if (deviceProfile == null) { - throw new DataValidationException("OtaPackage is referencing to non-existent device profile!"); - } - } - - if (otaPackageInfo.getType() == null) { - throw new DataValidationException("Type should be specified!"); - } - - if (StringUtils.isEmpty(otaPackageInfo.getTitle())) { - throw new DataValidationException("OtaPackage title should be specified!"); - } - - if (StringUtils.isEmpty(otaPackageInfo.getVersion())) { - throw new DataValidationException("OtaPackage version should be specified!"); - } - - if(otaPackageInfo.getTitle().length() > 255) { - throw new DataValidationException("The length of title should be equal or shorter than 255"); - } - - if(otaPackageInfo.getVersion().length() > 255) { - throw new DataValidationException("The length of version should be equal or shorter than 255"); - } - - } - private PaginatedRemover tenantOtaPackageRemover = new PaginatedRemover<>() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index c515e95f73..dd13f5b200 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -16,51 +16,37 @@ package org.thingsboard.server.dao.resource; import com.google.common.util.concurrent.ListenableFuture; +import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.hibernate.exception.ConstraintViolationException; -import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; -import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; -import org.thingsboard.server.dao.tenant.TbTenantProfileCache; -import org.thingsboard.server.dao.tenant.TenantDao; import java.util.List; import java.util.Optional; -import static org.thingsboard.server.common.data.EntityType.TB_RESOURCE; import static org.thingsboard.server.dao.device.DeviceServiceImpl.INCORRECT_TENANT_ID; import static org.thingsboard.server.dao.service.Validator.validateId; @Service @Slf4j +@AllArgsConstructor public class BaseResourceService implements ResourceService { public static final String INCORRECT_RESOURCE_ID = "Incorrect resourceId "; private final TbResourceDao resourceDao; private final TbResourceInfoDao resourceInfoDao; - private final TenantDao tenantDao; - private final TbTenantProfileCache tenantProfileCache; - - public BaseResourceService(TbResourceDao resourceDao, TbResourceInfoDao resourceInfoDao, TenantDao tenantDao, @Lazy TbTenantProfileCache tenantProfileCache) { - this.resourceDao = resourceDao; - this.resourceInfoDao = resourceInfoDao; - this.tenantDao = tenantDao; - this.tenantProfileCache = tenantProfileCache; - } + private final DataValidator resourceValidator; @Override public TbResource saveResource(TbResource resource) { @@ -153,45 +139,7 @@ public class BaseResourceService implements ResourceService { return resourceDao.sumDataSizeByTenantId(tenantId); } - private DataValidator resourceValidator = new DataValidator<>() { - - @Override - protected void validateCreate(TenantId tenantId, TbResource resource) { - if (tenantId != null && !TenantId.SYS_TENANT_ID.equals(tenantId) ) { - DefaultTenantProfileConfiguration profileConfiguration = - (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); - long maxSumResourcesDataInBytes = profileConfiguration.getMaxResourcesInBytes(); - validateMaxSumDataSizePerTenant(tenantId, resourceDao, maxSumResourcesDataInBytes, resource.getData().length(), TB_RESOURCE); - } - } - - @Override - protected void validateDataImpl(TenantId tenantId, TbResource resource) { - if (StringUtils.isEmpty(resource.getTitle())) { - throw new DataValidationException("Resource title should be specified!"); - } - if (resource.getResourceType() == null) { - throw new DataValidationException("Resource type should be specified!"); - } - if (StringUtils.isEmpty(resource.getFileName())) { - throw new DataValidationException("Resource file name should be specified!"); - } - if (StringUtils.isEmpty(resource.getResourceKey())) { - throw new DataValidationException("Resource key should be specified!"); - } - if (resource.getTenantId() == null) { - resource.setTenantId(TenantId.fromUUID(ModelConstants.NULL_UUID)); - } - if (!resource.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { - Tenant tenant = tenantDao.findById(tenantId, resource.getTenantId().getId()); - if (tenant == null) { - throw new DataValidationException("Resource is referencing to non-existent tenant!"); - } - } - } - }; - - private PaginatedRemover tenantResourcesRemover = + private final PaginatedRemover tenantResourcesRemover = new PaginatedRemover<>() { @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index b522cb8bf0..6916566307 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -21,16 +21,13 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.hibernate.exception.ConstraintViolationException; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; @@ -51,15 +48,12 @@ import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleChainUpdateResult; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.rule.RuleNodeUpdateResult; -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.ConstraintValidator; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; -import org.thingsboard.server.dao.tenant.TbTenantProfileCache; -import org.thingsboard.server.dao.tenant.TenantDao; import java.util.ArrayList; import java.util.Collection; @@ -94,11 +88,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC private RuleNodeDao ruleNodeDao; @Autowired - private TenantDao tenantDao; - - @Autowired - @Lazy - private TbTenantProfileCache tenantProfileCache; + private DataValidator ruleChainValidator; @Override @Transactional @@ -519,23 +509,6 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC } } - private List findAllTenantRuleChains(TenantId tenantId, RuleChainType type) { - PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE); - return findAllTenantRuleChainsRecursive(tenantId, new ArrayList<>(), type, pageLink); - } - - private List findAllTenantRuleChainsRecursive(TenantId tenantId, List accumulator, RuleChainType type, PageLink pageLink) { - PageData persistentRuleChainData = findTenantRuleChainsByType(tenantId, type, pageLink); - List ruleChains = persistentRuleChainData.getData(); - if (!CollectionUtils.isEmpty(ruleChains)) { - accumulator.addAll(ruleChains); - } - if (persistentRuleChainData.hasNext()) { - return findAllTenantRuleChainsRecursive(tenantId, accumulator, type, pageLink.nextPageLink()); - } - return accumulator; - } - private void setNewRuleChainId(RuleChain ruleChain, List metadata, RuleChainId oldRuleChainId, RuleChainId newRuleChainId) { ruleChain.setId(newRuleChainId); for (RuleChainMetaData metaData : metadata) { @@ -705,46 +678,6 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC ruleNodeDao.removeById(tenantId, entityId.getId()); } - private final DataValidator ruleChainValidator = - new DataValidator<>() { - @Override - protected void validateCreate(TenantId tenantId, RuleChain data) { - DefaultTenantProfileConfiguration profileConfiguration = - (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); - long maxRuleChains = profileConfiguration.getMaxRuleChains(); - validateNumberOfEntitiesPerTenant(tenantId, ruleChainDao, maxRuleChains, EntityType.RULE_CHAIN); - } - - @Override - protected void validateDataImpl(TenantId tenantId, RuleChain ruleChain) { - if (StringUtils.isEmpty(ruleChain.getName())) { - throw new DataValidationException("Rule chain name should be specified!"); - } - if (ruleChain.getType() == null) { - ruleChain.setType(RuleChainType.CORE); - } - if (ruleChain.getTenantId() == null || ruleChain.getTenantId().isNullUid()) { - throw new DataValidationException("Rule chain should be assigned to tenant!"); - } - Tenant tenant = tenantDao.findById(tenantId, ruleChain.getTenantId().getId()); - if (tenant == null) { - throw new DataValidationException("Rule chain is referencing to non-existent tenant!"); - } - if (ruleChain.isRoot() && RuleChainType.CORE.equals(ruleChain.getType())) { - RuleChain rootRuleChain = getRootTenantRuleChain(ruleChain.getTenantId()); - if (rootRuleChain != null && !rootRuleChain.getId().equals(ruleChain.getId())) { - throw new DataValidationException("Another root rule chain is present in scope of current tenant!"); - } - } - if (ruleChain.isRoot() && RuleChainType.EDGE.equals(ruleChain.getType())) { - RuleChain edgeTemplateRootRuleChain = getEdgeTemplateRootRuleChain(ruleChain.getTenantId()); - if (edgeTemplateRootRuleChain != null && !edgeTemplateRootRuleChain.getId().equals(ruleChain.getId())) { - throw new DataValidationException("Another edge template root rule chain is present in scope of current tenant!"); - } - } - } - }; - private final PaginatedRemover tenantRuleChainsRemover = new PaginatedRemover<>() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AdminSettingsDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AdminSettingsDataValidator.java new file mode 100644 index 0000000000..c1485cfb1a --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AdminSettingsDataValidator.java @@ -0,0 +1,61 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import lombok.AllArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.settings.AdminSettingsService; + +@Component +@AllArgsConstructor +public class AdminSettingsDataValidator extends DataValidator { + + private final AdminSettingsService adminSettingsService; + + @Override + protected void validateCreate(TenantId tenantId, AdminSettings adminSettings) { + AdminSettings existentAdminSettingsWithKey = adminSettingsService.findAdminSettingsByKey(tenantId, adminSettings.getKey()); + if (existentAdminSettingsWithKey != null) { + throw new DataValidationException("Admin settings with such name already exists!"); + } + } + + @Override + protected void validateUpdate(TenantId tenantId, AdminSettings adminSettings) { + AdminSettings existentAdminSettings = adminSettingsService.findAdminSettingsById(tenantId, adminSettings.getId()); + if (existentAdminSettings != null) { + if (!existentAdminSettings.getKey().equals(adminSettings.getKey())) { + throw new DataValidationException("Changing key of admin settings entry is prohibited!"); + } + } + } + + + @Override + protected void validateDataImpl(TenantId tenantId, AdminSettings adminSettings) { + if (StringUtils.isEmpty(adminSettings.getKey())) { + throw new DataValidationException("Key should be specified!"); + } + if (adminSettings.getJsonValue() == null) { + throw new DataValidationException("Json value should be specified!"); + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AlarmDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AlarmDataValidator.java new file mode 100644 index 0000000000..5bbe636dc7 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AlarmDataValidator.java @@ -0,0 +1,57 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TenantDao; + +@Component +@AllArgsConstructor +public class AlarmDataValidator extends DataValidator { + + private final TenantDao tenantDao; + + @Override + protected void validateDataImpl(TenantId tenantId, Alarm alarm) { + if (StringUtils.isEmpty(alarm.getType())) { + throw new DataValidationException("Alarm type should be specified!"); + } + if (alarm.getOriginator() == null) { + throw new DataValidationException("Alarm originator should be specified!"); + } + if (alarm.getSeverity() == null) { + throw new DataValidationException("Alarm severity should be specified!"); + } + if (alarm.getStatus() == null) { + throw new DataValidationException("Alarm status should be specified!"); + } + if (alarm.getTenantId() == null) { + throw new DataValidationException("Alarm should be assigned to tenant!"); + } else { + Tenant tenant = tenantDao.findById(alarm.getTenantId(), alarm.getTenantId().getId()); + if (tenant == null) { + throw new DataValidationException("Alarm is referencing to non-existent tenant!"); + } + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ApiUsageDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ApiUsageDataValidator.java new file mode 100644 index 0000000000..a89acb9631 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ApiUsageDataValidator.java @@ -0,0 +1,50 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.ApiUsageState; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TenantDao; + +@Component +@AllArgsConstructor +public class ApiUsageDataValidator extends DataValidator { + + private final TenantDao tenantDao; + + @Override + protected void validateDataImpl(TenantId requestTenantId, ApiUsageState apiUsageState) { + if (apiUsageState.getTenantId() == null) { + throw new DataValidationException("ApiUsageState should be assigned to tenant!"); + } else { + Tenant tenant = tenantDao.findById(requestTenantId, apiUsageState.getTenantId().getId()); + if (tenant == null && !requestTenantId.equals(TenantId.SYS_TENANT_ID)) { + throw new DataValidationException("ApiUsageState is referencing to non-existent tenant!"); + } + } + if (apiUsageState.getEntityId() == null) { + throw new DataValidationException("UsageRecord should be assigned to entity!"); + } else if (apiUsageState.getEntityId().getEntityType() != EntityType.TENANT && apiUsageState.getEntityId().getEntityType() != EntityType.CUSTOMER) { + throw new DataValidationException("Only Tenant and Customer Usage Records are supported!"); + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java new file mode 100644 index 0000000000..57d39ee781 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java @@ -0,0 +1,108 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.dao.asset.AssetDao; +import org.thingsboard.server.dao.asset.BaseAssetService; +import org.thingsboard.server.dao.cache.EntitiesCacheManager; +import org.thingsboard.server.dao.customer.CustomerDao; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; +import org.thingsboard.server.dao.tenant.TenantDao; + +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; + +@Component +public class AssetDataValidator extends DataValidator { + + @Autowired + private AssetDao assetDao; + + @Autowired + private TenantDao tenantDao; + + @Autowired + private CustomerDao customerDao; + + @Autowired + @Lazy + private TbTenantProfileCache tenantProfileCache; + + @Autowired + private EntitiesCacheManager cacheManager; + + @Override + protected void validateCreate(TenantId tenantId, Asset asset) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + if (!BaseAssetService.TB_SERVICE_QUEUE.equals(asset.getType())) { + long maxAssets = profileConfiguration.getMaxAssets(); + validateNumberOfEntitiesPerTenant(tenantId, assetDao, maxAssets, EntityType.ASSET); + } + } + + @Override + protected void validateUpdate(TenantId tenantId, Asset asset) { + Asset old = assetDao.findById(asset.getTenantId(), asset.getId().getId()); + if (old == null) { + throw new DataValidationException("Can't update non existing asset!"); + } + if (!old.getName().equals(asset.getName())) { + cacheManager.removeAssetFromCacheByName(tenantId, old.getName()); + } + } + + @Override + protected void validateDataImpl(TenantId tenantId, Asset asset) { + if (StringUtils.isEmpty(asset.getType())) { + throw new DataValidationException("Asset type should be specified!"); + } + if (StringUtils.isEmpty(asset.getName())) { + throw new DataValidationException("Asset name should be specified!"); + } + if (asset.getTenantId() == null) { + throw new DataValidationException("Asset should be assigned to tenant!"); + } else { + Tenant tenant = tenantDao.findById(tenantId, asset.getTenantId().getId()); + if (tenant == null) { + throw new DataValidationException("Asset is referencing to non-existent tenant!"); + } + } + if (asset.getCustomerId() == null) { + asset.setCustomerId(new CustomerId(NULL_UUID)); + } else if (!asset.getCustomerId().getId().equals(NULL_UUID)) { + Customer customer = customerDao.findById(tenantId, asset.getCustomerId().getId()); + if (customer == null) { + throw new DataValidationException("Can't assign asset to non-existent customer!"); + } + if (!customer.getTenantId().equals(asset.getTenantId())) { + throw new DataValidationException("Can't assign asset to customer from different tenant!"); + } + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AuditLogDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AuditLogDataValidator.java new file mode 100644 index 0000000000..619e229595 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AuditLogDataValidator.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.audit.AuditLog; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; + +@Component +public class AuditLogDataValidator extends DataValidator { + + @Override + protected void validateDataImpl(TenantId tenantId, AuditLog auditLog) { + if (auditLog.getEntityId() == null) { + throw new DataValidationException("Entity Id should be specified!"); + } + if (auditLog.getTenantId() == null) { + throw new DataValidationException("Tenant Id should be specified!"); + } + if (auditLog.getUserId() == null) { + throw new DataValidationException("User Id should be specified!"); + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/BaseOtaPackageDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/BaseOtaPackageDataValidator.java new file mode 100644 index 0000000000..8184ff8e8e --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/BaseOtaPackageDataValidator.java @@ -0,0 +1,123 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.dao.device.DeviceProfileDao; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TenantDao; + +import java.util.Objects; + +public abstract class BaseOtaPackageDataValidator> extends DataValidator { + + @Autowired + private TenantDao tenantDao; + + @Autowired + private DeviceProfileDao deviceProfileDao; + + protected void validateImpl(OtaPackageInfo otaPackageInfo) { + if (otaPackageInfo.getTenantId() == null) { + throw new DataValidationException("OtaPackage should be assigned to tenant!"); + } else { + Tenant tenant = tenantDao.findById(otaPackageInfo.getTenantId(), otaPackageInfo.getTenantId().getId()); + if (tenant == null) { + throw new DataValidationException("OtaPackage is referencing to non-existent tenant!"); + } + } + + if (otaPackageInfo.getDeviceProfileId() != null) { + DeviceProfile deviceProfile = deviceProfileDao.findById(otaPackageInfo.getTenantId(), otaPackageInfo.getDeviceProfileId().getId()); + if (deviceProfile == null) { + throw new DataValidationException("OtaPackage is referencing to non-existent device profile!"); + } + } + + if (otaPackageInfo.getType() == null) { + throw new DataValidationException("Type should be specified!"); + } + + if (StringUtils.isEmpty(otaPackageInfo.getTitle())) { + throw new DataValidationException("OtaPackage title should be specified!"); + } + + if (StringUtils.isEmpty(otaPackageInfo.getVersion())) { + throw new DataValidationException("OtaPackage version should be specified!"); + } + + if (otaPackageInfo.getTitle().length() > 255) { + throw new DataValidationException("The length of title should be equal or shorter than 255"); + } + + if (otaPackageInfo.getVersion().length() > 255) { + throw new DataValidationException("The length of version should be equal or shorter than 255"); + } + } + + protected void validateUpdate(OtaPackageInfo otaPackage, OtaPackageInfo otaPackageOld) { + if (!otaPackageOld.getType().equals(otaPackage.getType())) { + throw new DataValidationException("Updating type is prohibited!"); + } + + if (!otaPackageOld.getTitle().equals(otaPackage.getTitle())) { + throw new DataValidationException("Updating otaPackage title is prohibited!"); + } + + if (!otaPackageOld.getVersion().equals(otaPackage.getVersion())) { + throw new DataValidationException("Updating otaPackage version is prohibited!"); + } + + if (!Objects.equals(otaPackage.getTag(), otaPackageOld.getTag())) { + throw new DataValidationException("Updating otaPackage tag is prohibited!"); + } + + if (!otaPackageOld.getDeviceProfileId().equals(otaPackage.getDeviceProfileId())) { + throw new DataValidationException("Updating otaPackage deviceProfile is prohibited!"); + } + + if (otaPackageOld.getFileName() != null && !otaPackageOld.getFileName().equals(otaPackage.getFileName())) { + throw new DataValidationException("Updating otaPackage file name is prohibited!"); + } + + if (otaPackageOld.getContentType() != null && !otaPackageOld.getContentType().equals(otaPackage.getContentType())) { + throw new DataValidationException("Updating otaPackage content type is prohibited!"); + } + + if (otaPackageOld.getChecksumAlgorithm() != null && !otaPackageOld.getChecksumAlgorithm().equals(otaPackage.getChecksumAlgorithm())) { + throw new DataValidationException("Updating otaPackage content type is prohibited!"); + } + + if (otaPackageOld.getChecksum() != null && !otaPackageOld.getChecksum().equals(otaPackage.getChecksum())) { + throw new DataValidationException("Updating otaPackage content type is prohibited!"); + } + + if (otaPackageOld.getDataSize() != null && !otaPackageOld.getDataSize().equals(otaPackage.getDataSize())) { + throw new DataValidationException("Updating otaPackage data size is prohibited!"); + } + + if (otaPackageOld.getUrl() != null && !otaPackageOld.getUrl().equals(otaPackage.getUrl())) { + throw new DataValidationException("Updating otaPackage URL is prohibited!"); + } + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ClientRegistrationTemplateDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ClientRegistrationTemplateDataValidator.java new file mode 100644 index 0000000000..d558c19e9e --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ClientRegistrationTemplateDataValidator.java @@ -0,0 +1,51 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; + +@Component +public class ClientRegistrationTemplateDataValidator extends DataValidator { + + @Override + protected void validateCreate(TenantId tenantId, OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { + } + + @Override + protected void validateUpdate(TenantId tenantId, OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { + } + + @Override + protected void validateDataImpl(TenantId tenantId, OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { + if (StringUtils.isEmpty(clientRegistrationTemplate.getProviderId())) { + throw new DataValidationException("Provider ID should be specified!"); + } + if (clientRegistrationTemplate.getMapperConfig() == null) { + throw new DataValidationException("Mapper config should be specified!"); + } + if (clientRegistrationTemplate.getMapperConfig().getType() == null) { + throw new DataValidationException("Mapper type should be specified!"); + } + if (clientRegistrationTemplate.getMapperConfig().getBasic() == null) { + throw new DataValidationException("Basic mapper config should be specified!"); + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ComponentDescriptorDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ComponentDescriptorDataValidator.java new file mode 100644 index 0000000000..f7eceec4d5 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ComponentDescriptorDataValidator.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.plugin.ComponentDescriptor; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; + +@Component +public class ComponentDescriptorDataValidator extends DataValidator { + + @Override + protected void validateDataImpl(TenantId tenantId, ComponentDescriptor plugin) { + if (plugin.getType() == null) { + throw new DataValidationException("Component type should be specified!"); + } + if (plugin.getScope() == null) { + throw new DataValidationException("Component scope should be specified!"); + } + if (StringUtils.isEmpty(plugin.getName())) { + throw new DataValidationException("Component name should be specified!"); + } + if (StringUtils.isEmpty(plugin.getClazz())) { + throw new DataValidationException("Component clazz should be specified!"); + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/CustomerDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/CustomerDataValidator.java new file mode 100644 index 0000000000..753d3a8528 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/CustomerDataValidator.java @@ -0,0 +1,92 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.dao.customer.CustomerDao; +import org.thingsboard.server.dao.customer.CustomerServiceImpl; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; +import org.thingsboard.server.dao.tenant.TenantDao; + +@Component +public class CustomerDataValidator extends DataValidator { + + @Autowired + private CustomerDao customerDao; + + @Autowired + private TenantDao tenantDao; + + @Autowired + @Lazy + private TbTenantProfileCache tenantProfileCache; + + @Override + protected void validateCreate(TenantId tenantId, Customer customer) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxCustomers = profileConfiguration.getMaxCustomers(); + + validateNumberOfEntitiesPerTenant(tenantId, customerDao, maxCustomers, EntityType.CUSTOMER); + customerDao.findCustomersByTenantIdAndTitle(customer.getTenantId().getId(), customer.getTitle()).ifPresent( + c -> { + throw new DataValidationException("Customer with such title already exists!"); + } + ); + } + + @Override + protected void validateUpdate(TenantId tenantId, Customer customer) { + customerDao.findCustomersByTenantIdAndTitle(customer.getTenantId().getId(), customer.getTitle()).ifPresent( + c -> { + if (!c.getId().equals(customer.getId())) { + throw new DataValidationException("Customer with such title already exists!"); + } + } + ); + } + + @Override + protected void validateDataImpl(TenantId tenantId, Customer customer) { + if (StringUtils.isEmpty(customer.getTitle())) { + throw new DataValidationException("Customer title should be specified!"); + } + if (customer.getTitle().equals(CustomerServiceImpl.PUBLIC_CUSTOMER_TITLE)) { + throw new DataValidationException("'Public' title for customer is system reserved!"); + } + if (!StringUtils.isEmpty(customer.getEmail())) { + validateEmail(customer.getEmail()); + } + if (customer.getTenantId() == null) { + throw new DataValidationException("Customer should be assigned to tenant!"); + } else { + Tenant tenant = tenantDao.findById(tenantId, customer.getTenantId().getId()); + if (tenant == null) { + throw new DataValidationException("Customer is referencing to non-existent tenant!"); + } + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DashboardDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DashboardDataValidator.java new file mode 100644 index 0000000000..ee226af853 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DashboardDataValidator.java @@ -0,0 +1,68 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.dao.dashboard.DashboardDao; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; +import org.thingsboard.server.dao.tenant.TenantDao; + +@Component +public class DashboardDataValidator extends DataValidator { + + @Autowired + private DashboardDao dashboardDao; + + @Autowired + private TenantDao tenantDao; + + @Autowired + @Lazy + private TbTenantProfileCache tenantProfileCache; + + @Override + protected void validateCreate(TenantId tenantId, Dashboard data) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxDashboards = profileConfiguration.getMaxDashboards(); + validateNumberOfEntitiesPerTenant(tenantId, dashboardDao, maxDashboards, EntityType.DASHBOARD); + } + + @Override + protected void validateDataImpl(TenantId tenantId, Dashboard dashboard) { + if (StringUtils.isEmpty(dashboard.getTitle())) { + throw new DataValidationException("Dashboard title should be specified!"); + } + if (dashboard.getTenantId() == null) { + throw new DataValidationException("Dashboard should be assigned to tenant!"); + } else { + Tenant tenant = tenantDao.findById(tenantId, dashboard.getTenantId().getId()); + if (tenant == null) { + throw new DataValidationException("Dashboard is referencing to non-existent tenant!"); + } + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceCredentialsDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceCredentialsDataValidator.java new file mode 100644 index 0000000000..5fca062d46 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceCredentialsDataValidator.java @@ -0,0 +1,75 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.dao.device.DeviceCredentialsDao; +import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; +import org.thingsboard.server.dao.service.DataValidator; + +@Component +public class DeviceCredentialsDataValidator extends DataValidator { + + @Autowired + private DeviceCredentialsDao deviceCredentialsDao; + + @Autowired + private DeviceService deviceService; + + @Override + protected void validateCreate(TenantId tenantId, DeviceCredentials deviceCredentials) { + if (deviceCredentialsDao.findByDeviceId(tenantId, deviceCredentials.getDeviceId().getId()) != null) { + throw new DeviceCredentialsValidationException("Credentials for this device are already specified!"); + } + if (deviceCredentialsDao.findByCredentialsId(tenantId, deviceCredentials.getCredentialsId()) != null) { + throw new DeviceCredentialsValidationException("Device credentials are already assigned to another device!"); + } + } + + @Override + protected void validateUpdate(TenantId tenantId, DeviceCredentials deviceCredentials) { + if (deviceCredentialsDao.findById(tenantId, deviceCredentials.getUuidId()) == null) { + throw new DeviceCredentialsValidationException("Unable to update non-existent device credentials!"); + } + DeviceCredentials existingCredentials = deviceCredentialsDao.findByCredentialsId(tenantId, deviceCredentials.getCredentialsId()); + if (existingCredentials != null && !existingCredentials.getId().equals(deviceCredentials.getId())) { + throw new DeviceCredentialsValidationException("Device credentials are already assigned to another device!"); + } + } + + @Override + protected void validateDataImpl(TenantId tenantId, DeviceCredentials deviceCredentials) { + if (deviceCredentials.getDeviceId() == null) { + throw new DeviceCredentialsValidationException("Device credentials should be assigned to device!"); + } + if (deviceCredentials.getCredentialsType() == null) { + throw new DeviceCredentialsValidationException("Device credentials type should be specified!"); + } + if (StringUtils.isEmpty(deviceCredentials.getCredentialsId())) { + throw new DeviceCredentialsValidationException("Device credentials id should be specified!"); + } + Device device = deviceService.findDeviceById(tenantId, deviceCredentials.getDeviceId()); + if (device == null) { + throw new DeviceCredentialsValidationException("Can't assign device credentials to non-existent device!"); + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceDataValidator.java new file mode 100644 index 0000000000..fc991cee91 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceDataValidator.java @@ -0,0 +1,147 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.dao.cache.EntitiesCacheManager; +import org.thingsboard.server.dao.customer.CustomerDao; +import org.thingsboard.server.dao.device.DeviceDao; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.ota.OtaPackageService; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; +import org.thingsboard.server.dao.tenant.TenantDao; + +import java.util.Optional; + +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; + +@Component +public class DeviceDataValidator extends DataValidator { + + @Autowired + private DeviceDao deviceDao; + + @Autowired + private TenantDao tenantDao; + + @Autowired + private CustomerDao customerDao; + + @Autowired + @Lazy + private TbTenantProfileCache tenantProfileCache; + + @Autowired + private OtaPackageService otaPackageService; + + @Autowired + private EntitiesCacheManager cacheManager; + + @Override + protected void validateCreate(TenantId tenantId, Device device) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxDevices = profileConfiguration.getMaxDevices(); + validateNumberOfEntitiesPerTenant(tenantId, deviceDao, maxDevices, EntityType.DEVICE); + } + + @Override + protected void validateUpdate(TenantId tenantId, Device device) { + Device old = deviceDao.findById(device.getTenantId(), device.getId().getId()); + if (old == null) { + throw new DataValidationException("Can't update non existing device!"); + } + if (!old.getName().equals(device.getName())) { + cacheManager.removeDeviceFromCacheByName(tenantId, old.getName()); + cacheManager.removeDeviceFromCacheById(tenantId, device.getId()); + } + } + + @Override + protected void validateDataImpl(TenantId tenantId, Device device) { + if (StringUtils.isEmpty(device.getName()) || device.getName().trim().length() == 0) { + throw new DataValidationException("Device name should be specified!"); + } + if (device.getTenantId() == null) { + throw new DataValidationException("Device should be assigned to tenant!"); + } else { + Tenant tenant = tenantDao.findById(device.getTenantId(), device.getTenantId().getId()); + if (tenant == null) { + throw new DataValidationException("Device is referencing to non-existent tenant!"); + } + } + if (device.getCustomerId() == null) { + device.setCustomerId(new CustomerId(NULL_UUID)); + } else if (!device.getCustomerId().getId().equals(NULL_UUID)) { + Customer customer = customerDao.findById(device.getTenantId(), device.getCustomerId().getId()); + if (customer == null) { + throw new DataValidationException("Can't assign device to non-existent customer!"); + } + if (!customer.getTenantId().getId().equals(device.getTenantId().getId())) { + throw new DataValidationException("Can't assign device to customer from different tenant!"); + } + } + Optional.ofNullable(device.getDeviceData()) + .flatMap(deviceData -> Optional.ofNullable(deviceData.getTransportConfiguration())) + .ifPresent(DeviceTransportConfiguration::validate); + + if (device.getFirmwareId() != null) { + OtaPackage firmware = otaPackageService.findOtaPackageById(tenantId, device.getFirmwareId()); + if (firmware == null) { + throw new DataValidationException("Can't assign non-existent firmware!"); + } + if (!firmware.getType().equals(OtaPackageType.FIRMWARE)) { + throw new DataValidationException("Can't assign firmware with type: " + firmware.getType()); + } + if (firmware.getData() == null && !firmware.hasUrl()) { + throw new DataValidationException("Can't assign firmware with empty data!"); + } + if (!firmware.getDeviceProfileId().equals(device.getDeviceProfileId())) { + throw new DataValidationException("Can't assign firmware with different deviceProfile!"); + } + } + + if (device.getSoftwareId() != null) { + OtaPackage software = otaPackageService.findOtaPackageById(tenantId, device.getSoftwareId()); + if (software == null) { + throw new DataValidationException("Can't assign non-existent software!"); + } + if (!software.getType().equals(OtaPackageType.SOFTWARE)) { + throw new DataValidationException("Can't assign software with type: " + software.getType()); + } + if (software.getData() == null && !software.hasUrl()) { + throw new DataValidationException("Can't assign software with empty data!"); + } + if (!software.getDeviceProfileId().equals(device.getDeviceProfileId())) { + throw new DataValidationException("Can't assign firmware with different deviceProfile!"); + } + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java new file mode 100644 index 0000000000..adc8cf9fb1 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java @@ -0,0 +1,524 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import com.google.protobuf.Descriptors; +import com.google.protobuf.DynamicMessage; +import com.squareup.wire.Syntax; +import com.squareup.wire.schema.Field; +import com.squareup.wire.schema.Location; +import com.squareup.wire.schema.internal.parser.EnumElement; +import com.squareup.wire.schema.internal.parser.FieldElement; +import com.squareup.wire.schema.internal.parser.MessageElement; +import com.squareup.wire.schema.internal.parser.OneOfElement; +import com.squareup.wire.schema.internal.parser.ProtoFileElement; +import com.squareup.wire.schema.internal.parser.ProtoParser; +import com.squareup.wire.schema.internal.parser.TypeElement; +import org.eclipse.leshan.core.util.SecurityUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.thingsboard.server.common.data.DashboardInfo; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.DeviceProfileProvisionType; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode; +import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration; +import org.thingsboard.server.common.data.device.profile.DefaultCoapDeviceTypeConfiguration; +import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; +import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.AbstractLwM2MBootstrapServerCredential; +import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServerCredential; +import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.RPKLwM2MBootstrapServerCredential; +import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.X509LwM2MBootstrapServerCredential; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.msg.EncryptionUtil; +import org.thingsboard.server.common.msg.queue.ServiceType; +import org.thingsboard.server.dao.dashboard.DashboardService; +import org.thingsboard.server.dao.device.DeviceDao; +import org.thingsboard.server.dao.device.DeviceProfileDao; +import org.thingsboard.server.dao.device.DeviceProfileService; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; +import org.thingsboard.server.dao.ota.OtaPackageService; +import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TenantDao; +import org.thingsboard.server.queue.QueueService; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +@Component +public class DeviceProfileDataValidator extends DataValidator { + + private static final Location LOCATION = new Location("", "", -1, -1); + private static final String ATTRIBUTES_PROTO_SCHEMA = "attributes proto schema"; + private static final String TELEMETRY_PROTO_SCHEMA = "telemetry proto schema"; + private static final String RPC_REQUEST_PROTO_SCHEMA = "rpc request proto schema"; + private static final String RPC_RESPONSE_PROTO_SCHEMA = "rpc response proto schema"; + @Autowired + private DeviceProfileDao deviceProfileDao; + @Autowired + @Lazy + private DeviceProfileService deviceProfileService; + @Autowired + private DeviceDao deviceDao; + @Autowired + private TenantDao tenantDao; + @Autowired + @Lazy + private QueueService queueService; + @Autowired + private OtaPackageService otaPackageService; + @Autowired + private RuleChainService ruleChainService; + @Autowired + private DashboardService dashboardService; + + private static String invalidSchemaProvidedMessage(String schemaName) { + return "[Transport Configuration] invalid " + schemaName + " provided!"; + } + + @Override + protected void validateDataImpl(TenantId tenantId, DeviceProfile deviceProfile) { + if (org.thingsboard.server.common.data.StringUtils.isEmpty(deviceProfile.getName())) { + throw new DataValidationException("Device profile name should be specified!"); + } + if (deviceProfile.getType() == null) { + throw new DataValidationException("Device profile type should be specified!"); + } + if (deviceProfile.getTransportType() == null) { + throw new DataValidationException("Device profile transport type should be specified!"); + } + if (deviceProfile.getTenantId() == null) { + throw new DataValidationException("Device profile should be assigned to tenant!"); + } else { + Tenant tenant = tenantDao.findById(deviceProfile.getTenantId(), deviceProfile.getTenantId().getId()); + if (tenant == null) { + throw new DataValidationException("Device profile is referencing to non-existent tenant!"); + } + } + if (deviceProfile.isDefault()) { + DeviceProfile defaultDeviceProfile = deviceProfileService.findDefaultDeviceProfile(tenantId); + if (defaultDeviceProfile != null && !defaultDeviceProfile.getId().equals(deviceProfile.getId())) { + throw new DataValidationException("Another default device profile is present in scope of current tenant!"); + } + } + if (!org.thingsboard.server.common.data.StringUtils.isEmpty(deviceProfile.getDefaultQueueName()) && queueService != null) { + if (!queueService.getQueuesByServiceType(ServiceType.TB_RULE_ENGINE).contains(deviceProfile.getDefaultQueueName())) { + throw new DataValidationException("Device profile is referencing to non-existent queue!"); + } + } + if (deviceProfile.getProvisionType() == null) { + deviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED); + } + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); + transportConfiguration.validate(); + if (transportConfiguration instanceof MqttDeviceProfileTransportConfiguration) { + MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration; + if (mqttTransportConfiguration.getTransportPayloadTypeConfiguration() instanceof ProtoTransportPayloadConfiguration) { + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = + (ProtoTransportPayloadConfiguration) mqttTransportConfiguration.getTransportPayloadTypeConfiguration(); + validateProtoSchemas(protoTransportPayloadConfiguration); + validateTelemetryDynamicMessageFields(protoTransportPayloadConfiguration); + validateRpcRequestDynamicMessageFields(protoTransportPayloadConfiguration); + } + } else if (transportConfiguration instanceof CoapDeviceProfileTransportConfiguration) { + CoapDeviceProfileTransportConfiguration coapDeviceProfileTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration; + CoapDeviceTypeConfiguration coapDeviceTypeConfiguration = coapDeviceProfileTransportConfiguration.getCoapDeviceTypeConfiguration(); + if (coapDeviceTypeConfiguration instanceof DefaultCoapDeviceTypeConfiguration) { + DefaultCoapDeviceTypeConfiguration defaultCoapDeviceTypeConfiguration = (DefaultCoapDeviceTypeConfiguration) coapDeviceTypeConfiguration; + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = defaultCoapDeviceTypeConfiguration.getTransportPayloadTypeConfiguration(); + if (transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration) { + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; + validateProtoSchemas(protoTransportPayloadConfiguration); + validateTelemetryDynamicMessageFields(protoTransportPayloadConfiguration); + validateRpcRequestDynamicMessageFields(protoTransportPayloadConfiguration); + } + } + } else if (transportConfiguration instanceof Lwm2mDeviceProfileTransportConfiguration) { + List lwM2MBootstrapServersConfigurations = ((Lwm2mDeviceProfileTransportConfiguration) transportConfiguration).getBootstrap(); + if (lwM2MBootstrapServersConfigurations != null) { + validateLwm2mServersConfigOfBootstrapForClient(lwM2MBootstrapServersConfigurations, + ((Lwm2mDeviceProfileTransportConfiguration) transportConfiguration).isBootstrapServerUpdateEnable()); + for (LwM2MBootstrapServerCredential bootstrapServerCredential : lwM2MBootstrapServersConfigurations) { + validateLwm2mServersCredentialOfBootstrapForClient(bootstrapServerCredential); + } + } + } + + List profileAlarms = deviceProfile.getProfileData().getAlarms(); + + if (!CollectionUtils.isEmpty(profileAlarms)) { + Set alarmTypes = new HashSet<>(); + for (DeviceProfileAlarm alarm : profileAlarms) { + String alarmType = alarm.getAlarmType(); + if (StringUtils.isEmpty(alarmType)) { + throw new DataValidationException("Alarm rule type should be specified!"); + } + if (!alarmTypes.add(alarmType)) { + throw new DataValidationException(String.format("Can't create device profile with the same alarm rule types: \"%s\"!", alarmType)); + } + } + } + + if (deviceProfile.getDefaultRuleChainId() != null) { + RuleChain ruleChain = ruleChainService.findRuleChainById(tenantId, deviceProfile.getDefaultRuleChainId()); + if (ruleChain == null) { + throw new DataValidationException("Can't assign non-existent rule chain!"); + } + } + + if (deviceProfile.getDefaultDashboardId() != null) { + DashboardInfo dashboard = dashboardService.findDashboardInfoById(tenantId, deviceProfile.getDefaultDashboardId()); + if (dashboard == null) { + throw new DataValidationException("Can't assign non-existent dashboard!"); + } + } + + if (deviceProfile.getFirmwareId() != null) { + OtaPackage firmware = otaPackageService.findOtaPackageById(tenantId, deviceProfile.getFirmwareId()); + if (firmware == null) { + throw new DataValidationException("Can't assign non-existent firmware!"); + } + if (!firmware.getType().equals(OtaPackageType.FIRMWARE)) { + throw new DataValidationException("Can't assign firmware with type: " + firmware.getType()); + } + if (firmware.getData() == null && !firmware.hasUrl()) { + throw new DataValidationException("Can't assign firmware with empty data!"); + } + if (!firmware.getDeviceProfileId().equals(deviceProfile.getId())) { + throw new DataValidationException("Can't assign firmware with different deviceProfile!"); + } + } + + if (deviceProfile.getSoftwareId() != null) { + OtaPackage software = otaPackageService.findOtaPackageById(tenantId, deviceProfile.getSoftwareId()); + if (software == null) { + throw new DataValidationException("Can't assign non-existent software!"); + } + if (!software.getType().equals(OtaPackageType.SOFTWARE)) { + throw new DataValidationException("Can't assign software with type: " + software.getType()); + } + if (software.getData() == null && !software.hasUrl()) { + throw new DataValidationException("Can't assign software with empty data!"); + } + if (!software.getDeviceProfileId().equals(deviceProfile.getId())) { + throw new DataValidationException("Can't assign firmware with different deviceProfile!"); + } + } + } + + @Override + protected void validateUpdate(TenantId tenantId, DeviceProfile deviceProfile) { + DeviceProfile old = deviceProfileDao.findById(deviceProfile.getTenantId(), deviceProfile.getId().getId()); + if (old == null) { + throw new DataValidationException("Can't update non existing device profile!"); + } + boolean profileTypeChanged = !old.getType().equals(deviceProfile.getType()); + boolean transportTypeChanged = !old.getTransportType().equals(deviceProfile.getTransportType()); + if (profileTypeChanged || transportTypeChanged) { + Long profileDeviceCount = deviceDao.countDevicesByDeviceProfileId(deviceProfile.getTenantId(), deviceProfile.getId().getId()); + if (profileDeviceCount > 0) { + String message = null; + if (profileTypeChanged) { + message = "Can't change device profile type because devices referenced it!"; + } else if (transportTypeChanged) { + message = "Can't change device profile transport type because devices referenced it!"; + } + throw new DataValidationException(message); + } + } + } + + private void validateProtoSchemas(ProtoTransportPayloadConfiguration protoTransportPayloadTypeConfiguration) { + try { + validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceAttributesProtoSchema(), ATTRIBUTES_PROTO_SCHEMA); + validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceTelemetryProtoSchema(), TELEMETRY_PROTO_SCHEMA); + validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceRpcRequestProtoSchema(), RPC_REQUEST_PROTO_SCHEMA); + validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceRpcResponseProtoSchema(), RPC_RESPONSE_PROTO_SCHEMA); + } catch (Exception exception) { + throw new DataValidationException(exception.getMessage()); + } + } + + private void validateTransportProtoSchema(String schema, String schemaName) throws IllegalArgumentException { + ProtoParser schemaParser = new ProtoParser(LOCATION, schema.toCharArray()); + ProtoFileElement protoFileElement; + try { + protoFileElement = schemaParser.readProtoFile(); + } catch (Exception e) { + throw new IllegalArgumentException("[Transport Configuration] failed to parse " + schemaName + " due to: " + e.getMessage()); + } + checkProtoFileSyntax(schemaName, protoFileElement); + checkProtoFileCommonSettings(schemaName, protoFileElement.getOptions().isEmpty(), " Schema options don't support!"); + checkProtoFileCommonSettings(schemaName, protoFileElement.getPublicImports().isEmpty(), " Schema public imports don't support!"); + checkProtoFileCommonSettings(schemaName, protoFileElement.getImports().isEmpty(), " Schema imports don't support!"); + checkProtoFileCommonSettings(schemaName, protoFileElement.getExtendDeclarations().isEmpty(), " Schema extend declarations don't support!"); + checkTypeElements(schemaName, protoFileElement); + } + + private void checkProtoFileSyntax(String schemaName, ProtoFileElement protoFileElement) { + if (protoFileElement.getSyntax() == null || !protoFileElement.getSyntax().equals(Syntax.PROTO_3)) { + throw new IllegalArgumentException("[Transport Configuration] invalid schema syntax: " + protoFileElement.getSyntax() + + " for " + schemaName + " provided! Only " + Syntax.PROTO_3 + " allowed!"); + } + } + + private void checkProtoFileCommonSettings(String schemaName, boolean isEmptySettings, String invalidSettingsMessage) { + if (!isEmptySettings) { + throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + invalidSettingsMessage); + } + } + + private void checkTypeElements(String schemaName, ProtoFileElement protoFileElement) { + List types = protoFileElement.getTypes(); + if (!types.isEmpty()) { + if (types.stream().noneMatch(typeElement -> typeElement instanceof MessageElement)) { + throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " At least one Message definition should exists!"); + } else { + checkEnumElements(schemaName, getEnumElements(types)); + checkMessageElements(schemaName, getMessageTypes(types)); + } + } else { + throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Type elements is empty!"); + } + } + + private void checkFieldElements(String schemaName, List fieldElements) { + if (!fieldElements.isEmpty()) { + boolean hasRequiredLabel = fieldElements.stream().anyMatch(fieldElement -> { + Field.Label label = fieldElement.getLabel(); + return label != null && label.equals(Field.Label.REQUIRED); + }); + if (hasRequiredLabel) { + throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Required labels are not supported!"); + } + boolean hasDefaultValue = fieldElements.stream().anyMatch(fieldElement -> fieldElement.getDefaultValue() != null); + if (hasDefaultValue) { + throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Default values are not supported!"); + } + } + } + + private void checkEnumElements(String schemaName, List enumTypes) { + if (enumTypes.stream().anyMatch(enumElement -> !enumElement.getNestedTypes().isEmpty())) { + throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Nested types in Enum definitions are not supported!"); + } + if (enumTypes.stream().anyMatch(enumElement -> !enumElement.getOptions().isEmpty())) { + throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Enum definitions options are not supported!"); + } + } + + private void checkMessageElements(String schemaName, List messageElementsList) { + if (!messageElementsList.isEmpty()) { + messageElementsList.forEach(messageElement -> { + checkProtoFileCommonSettings(schemaName, messageElement.getGroups().isEmpty(), + " Message definition groups don't support!"); + checkProtoFileCommonSettings(schemaName, messageElement.getOptions().isEmpty(), + " Message definition options don't support!"); + checkProtoFileCommonSettings(schemaName, messageElement.getExtensions().isEmpty(), + " Message definition extensions don't support!"); + checkProtoFileCommonSettings(schemaName, messageElement.getReserveds().isEmpty(), + " Message definition reserved elements don't support!"); + checkFieldElements(schemaName, messageElement.getFields()); + List oneOfs = messageElement.getOneOfs(); + if (!oneOfs.isEmpty()) { + oneOfs.forEach(oneOfElement -> { + checkProtoFileCommonSettings(schemaName, oneOfElement.getGroups().isEmpty(), + " OneOf definition groups don't support!"); + checkFieldElements(schemaName, oneOfElement.getFields()); + }); + } + List nestedTypes = messageElement.getNestedTypes(); + if (!nestedTypes.isEmpty()) { + List nestedEnumTypes = getEnumElements(nestedTypes); + if (!nestedEnumTypes.isEmpty()) { + checkEnumElements(schemaName, nestedEnumTypes); + } + List nestedMessageTypes = getMessageTypes(nestedTypes); + checkMessageElements(schemaName, nestedMessageTypes); + } + }); + } + } + + private List getMessageTypes(List types) { + return types.stream() + .filter(typeElement -> typeElement instanceof MessageElement) + .map(typeElement -> (MessageElement) typeElement) + .collect(Collectors.toList()); + } + + private List getEnumElements(List types) { + return types.stream() + .filter(typeElement -> typeElement instanceof EnumElement) + .map(typeElement -> (EnumElement) typeElement) + .collect(Collectors.toList()); + } + + private void validateTelemetryDynamicMessageFields(ProtoTransportPayloadConfiguration protoTransportPayloadTypeConfiguration) { + String deviceTelemetryProtoSchema = protoTransportPayloadTypeConfiguration.getDeviceTelemetryProtoSchema(); + Descriptors.Descriptor telemetryDynamicMessageDescriptor = protoTransportPayloadTypeConfiguration.getTelemetryDynamicMessageDescriptor(deviceTelemetryProtoSchema); + if (telemetryDynamicMessageDescriptor == null) { + throw new DataValidationException(invalidSchemaProvidedMessage(TELEMETRY_PROTO_SCHEMA) + " Failed to get telemetryDynamicMessageDescriptor!"); + } else { + List fields = telemetryDynamicMessageDescriptor.getFields(); + if (CollectionUtils.isEmpty(fields)) { + throw new DataValidationException(invalidSchemaProvidedMessage(TELEMETRY_PROTO_SCHEMA) + " " + telemetryDynamicMessageDescriptor.getName() + " fields is empty!"); + } else if (fields.size() == 2) { + Descriptors.FieldDescriptor tsFieldDescriptor = telemetryDynamicMessageDescriptor.findFieldByName("ts"); + Descriptors.FieldDescriptor valuesFieldDescriptor = telemetryDynamicMessageDescriptor.findFieldByName("values"); + if (tsFieldDescriptor != null && valuesFieldDescriptor != null) { + if (!Descriptors.FieldDescriptor.Type.MESSAGE.equals(valuesFieldDescriptor.getType())) { + throw new DataValidationException(invalidSchemaProvidedMessage(TELEMETRY_PROTO_SCHEMA) + " Field 'values' has invalid data type. Only message type is supported!"); + } + if (!Descriptors.FieldDescriptor.Type.INT64.equals(tsFieldDescriptor.getType())) { + throw new DataValidationException(invalidSchemaProvidedMessage(TELEMETRY_PROTO_SCHEMA) + " Field 'ts' has invalid data type. Only int64 type is supported!"); + } + if (!tsFieldDescriptor.hasOptionalKeyword()) { + throw new DataValidationException(invalidSchemaProvidedMessage(TELEMETRY_PROTO_SCHEMA) + " Field 'ts' has invalid label. Field 'ts' should have optional keyword!"); + } + } + } + } + } + + private void validateRpcRequestDynamicMessageFields(ProtoTransportPayloadConfiguration protoTransportPayloadTypeConfiguration) { + DynamicMessage.Builder rpcRequestDynamicMessageBuilder = protoTransportPayloadTypeConfiguration.getRpcRequestDynamicMessageBuilder(protoTransportPayloadTypeConfiguration.getDeviceRpcRequestProtoSchema()); + Descriptors.Descriptor rpcRequestDynamicMessageDescriptor = rpcRequestDynamicMessageBuilder.getDescriptorForType(); + if (rpcRequestDynamicMessageDescriptor == null) { + throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Failed to get rpcRequestDynamicMessageDescriptor!"); + } else { + if (CollectionUtils.isEmpty(rpcRequestDynamicMessageDescriptor.getFields()) || rpcRequestDynamicMessageDescriptor.getFields().size() != 3) { + throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " " + rpcRequestDynamicMessageDescriptor.getName() + " message should always contains 3 fields: method, requestId and params!"); + } + Descriptors.FieldDescriptor methodFieldDescriptor = rpcRequestDynamicMessageDescriptor.findFieldByName("method"); + if (methodFieldDescriptor == null) { + throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Failed to get field descriptor for field: method!"); + } else { + if (!Descriptors.FieldDescriptor.Type.STRING.equals(methodFieldDescriptor.getType())) { + throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Field 'method' has invalid data type. Only string type is supported!"); + } + if (methodFieldDescriptor.isRepeated()) { + throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Field 'method' has invalid label!"); + } + } + Descriptors.FieldDescriptor requestIdFieldDescriptor = rpcRequestDynamicMessageDescriptor.findFieldByName("requestId"); + if (requestIdFieldDescriptor == null) { + throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Failed to get field descriptor for field: requestId!"); + } else { + if (!Descriptors.FieldDescriptor.Type.INT32.equals(requestIdFieldDescriptor.getType())) { + throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Field 'requestId' has invalid data type. Only int32 type is supported!"); + } + if (requestIdFieldDescriptor.isRepeated()) { + throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Field 'requestId' has invalid label!"); + } + } + Descriptors.FieldDescriptor paramsFieldDescriptor = rpcRequestDynamicMessageDescriptor.findFieldByName("params"); + if (paramsFieldDescriptor == null) { + throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Failed to get field descriptor for field: params!"); + } else { + if (paramsFieldDescriptor.isRepeated()) { + throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Field 'params' has invalid label!"); + } + } + } + } + + private void validateLwm2mServersConfigOfBootstrapForClient(List lwM2MBootstrapServersConfigurations, boolean isBootstrapServerUpdateEnable) { + Set uris = new HashSet<>(); + Set shortServerIds = new HashSet<>(); + for (LwM2MBootstrapServerCredential bootstrapServerCredential : lwM2MBootstrapServersConfigurations) { + AbstractLwM2MBootstrapServerCredential serverConfig = (AbstractLwM2MBootstrapServerCredential) bootstrapServerCredential; + if (!isBootstrapServerUpdateEnable && serverConfig.isBootstrapServerIs()) { + throw new DeviceCredentialsValidationException("Bootstrap config must not include \"Bootstrap Server\". \"Include Bootstrap Server updates\" is " + isBootstrapServerUpdateEnable + "."); + } + String server = serverConfig.isBootstrapServerIs() ? "Bootstrap Server" : "LwM2M Server" + " shortServerId: " + serverConfig.getShortServerId() + ":"; + if (serverConfig.getShortServerId() < 1 || serverConfig.getShortServerId() > 65534) { + throw new DeviceCredentialsValidationException(server + " ShortServerId must not be less than 1 and more than 65534!"); + } + if (!shortServerIds.add(serverConfig.getShortServerId())) { + throw new DeviceCredentialsValidationException(server + " \"Short server Id\" value = " + serverConfig.getShortServerId() + ". This value must be a unique value for all servers!"); + } + String uri = serverConfig.getHost() + ":" + serverConfig.getPort(); + if (!uris.add(uri)) { + throw new DeviceCredentialsValidationException(server + " \"Host + port\" value = " + uri + ". This value must be a unique value for all servers!"); + } + Integer port; + if (LwM2MSecurityMode.NO_SEC.equals(serverConfig.getSecurityMode())) { + port = serverConfig.isBootstrapServerIs() ? 5687 : 5685; + } else { + port = serverConfig.isBootstrapServerIs() ? 5688 : 5686; + } + if (serverConfig.getPort() == null || serverConfig.getPort().intValue() != port) { + String errMsg = server + " \"Port\" value = " + serverConfig.getPort() + ". This value for security " + serverConfig.getSecurityMode().name() + " must be " + port + "!"; + throw new DeviceCredentialsValidationException(errMsg); + } + } + } + + private void validateLwm2mServersCredentialOfBootstrapForClient(LwM2MBootstrapServerCredential bootstrapServerConfig) { + String server; + switch (bootstrapServerConfig.getSecurityMode()) { + case NO_SEC: + case PSK: + break; + case RPK: + RPKLwM2MBootstrapServerCredential rpkServerCredentials = (RPKLwM2MBootstrapServerCredential) bootstrapServerConfig; + server = rpkServerCredentials.isBootstrapServerIs() ? "Bootstrap Server" : "LwM2M Server"; + if (StringUtils.isEmpty(rpkServerCredentials.getServerPublicKey())) { + throw new DeviceCredentialsValidationException(server + " RPK public key must be specified!"); + } + try { + String pubkRpkSever = EncryptionUtil.pubkTrimNewLines(rpkServerCredentials.getServerPublicKey()); + rpkServerCredentials.setServerPublicKey(pubkRpkSever); + SecurityUtil.publicKey.decode(rpkServerCredentials.getDecodedCServerPublicKey()); + } catch (Exception e) { + throw new DeviceCredentialsValidationException(server + " RPK public key must be in standard [RFC7250] and then encoded to Base64 format!"); + } + break; + case X509: + X509LwM2MBootstrapServerCredential x509ServerCredentials = (X509LwM2MBootstrapServerCredential) bootstrapServerConfig; + server = x509ServerCredentials.isBootstrapServerIs() ? "Bootstrap Server" : "LwM2M Server"; + if (StringUtils.isEmpty(x509ServerCredentials.getServerPublicKey())) { + throw new DeviceCredentialsValidationException(server + " X509 certificate must be specified!"); + } + + try { + String certServer = EncryptionUtil.certTrimNewLines(x509ServerCredentials.getServerPublicKey()); + x509ServerCredentials.setServerPublicKey(certServer); + SecurityUtil.certificate.decode(x509ServerCredentials.getDecodedCServerPublicKey()); + } catch (Exception e) { + throw new DeviceCredentialsValidationException(server + " X509 certificate must be in DER-encoded X509v3 format and support only EC algorithm and then encoded to Base64 format!"); + } + break; + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/EdgeDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/EdgeDataValidator.java new file mode 100644 index 0000000000..be01ec5c9b --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/EdgeDataValidator.java @@ -0,0 +1,90 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.cache.EntitiesCacheManager; +import org.thingsboard.server.dao.customer.CustomerDao; +import org.thingsboard.server.dao.edge.EdgeDao; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TenantDao; + +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; + +@Component +@AllArgsConstructor +public class EdgeDataValidator extends DataValidator { + + private final EdgeDao edgeDao; + private final TenantDao tenantDao; + private final CustomerDao customerDao; + private final EntitiesCacheManager cacheManager; + + @Override + protected void validateCreate(TenantId tenantId, Edge edge) { + } + + @Override + protected void validateUpdate(TenantId tenantId, Edge edge) { + Edge old = edgeDao.findById(edge.getTenantId(), edge.getId().getId()); + if (!old.getName().equals(edge.getName())) { + cacheManager.removeEdgeFromCacheByName(tenantId, old.getName()); + } + } + + @Override + protected void validateDataImpl(TenantId tenantId, Edge edge) { + if (org.springframework.util.StringUtils.isEmpty(edge.getType())) { + throw new DataValidationException("Edge type should be specified!"); + } + if (org.springframework.util.StringUtils.isEmpty(edge.getName())) { + throw new DataValidationException("Edge name should be specified!"); + } + if (org.springframework.util.StringUtils.isEmpty(edge.getSecret())) { + throw new DataValidationException("Edge secret should be specified!"); + } + if (StringUtils.isEmpty(edge.getRoutingKey())) { + throw new DataValidationException("Edge routing key should be specified!"); + } + if (edge.getTenantId() == null) { + throw new DataValidationException("Edge should be assigned to tenant!"); + } else { + Tenant tenant = tenantDao.findById(edge.getTenantId(), edge.getTenantId().getId()); + if (tenant == null) { + throw new DataValidationException("Edge is referencing to non-existent tenant!"); + } + } + if (edge.getCustomerId() == null) { + edge.setCustomerId(new CustomerId(NULL_UUID)); + } else if (!edge.getCustomerId().getId().equals(NULL_UUID)) { + Customer customer = customerDao.findById(edge.getTenantId(), edge.getCustomerId().getId()); + if (customer == null) { + throw new DataValidationException("Can't assign edge to non-existent customer!"); + } + if (!customer.getTenantId().getId().equals(edge.getTenantId().getId())) { + throw new DataValidationException("Can't assign edge to customer from different tenant!"); + } + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/EdgeEventDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/EdgeEventDataValidator.java new file mode 100644 index 0000000000..5c437401b3 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/EdgeEventDataValidator.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; + +@Component +public class EdgeEventDataValidator extends DataValidator { + + @Override + protected void validateDataImpl(TenantId tenantId, EdgeEvent edgeEvent) { + if (edgeEvent.getEdgeId() == null) { + throw new DataValidationException("Edge id should be specified!"); + } + if (edgeEvent.getAction() == null) { + throw new DataValidationException("Edge Event action should be specified!"); + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/EntityViewDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/EntityViewDataValidator.java new file mode 100644 index 0000000000..a62e1afc9f --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/EntityViewDataValidator.java @@ -0,0 +1,88 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import lombok.AllArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.customer.CustomerDao; +import org.thingsboard.server.dao.entityview.EntityViewDao; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TenantDao; + +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; + +@Component +@AllArgsConstructor +public class EntityViewDataValidator extends DataValidator { + + private final EntityViewDao entityViewDao; + private final TenantDao tenantDao; + private final CustomerDao customerDao; + + @Override + protected void validateCreate(TenantId tenantId, EntityView entityView) { + entityViewDao.findEntityViewByTenantIdAndName(entityView.getTenantId().getId(), entityView.getName()) + .ifPresent(e -> { + throw new DataValidationException("Entity view with such name already exists!"); + }); + } + + @Override + protected void validateUpdate(TenantId tenantId, EntityView entityView) { + entityViewDao.findEntityViewByTenantIdAndName(entityView.getTenantId().getId(), entityView.getName()) + .ifPresent(e -> { + if (!e.getUuidId().equals(entityView.getUuidId())) { + throw new DataValidationException("Entity view with such name already exists!"); + } + }); + } + + @Override + protected void validateDataImpl(TenantId tenantId, EntityView entityView) { + if (StringUtils.isEmpty(entityView.getType())) { + throw new DataValidationException("Entity View type should be specified!"); + } + if (StringUtils.isEmpty(entityView.getName())) { + throw new DataValidationException("Entity view name should be specified!"); + } + if (entityView.getTenantId() == null) { + throw new DataValidationException("Entity view should be assigned to tenant!"); + } else { + Tenant tenant = tenantDao.findById(tenantId, entityView.getTenantId().getId()); + if (tenant == null) { + throw new DataValidationException("Entity view is referencing to non-existent tenant!"); + } + } + if (entityView.getCustomerId() == null) { + entityView.setCustomerId(new CustomerId(NULL_UUID)); + } else if (!entityView.getCustomerId().getId().equals(NULL_UUID)) { + Customer customer = customerDao.findById(tenantId, entityView.getCustomerId().getId()); + if (customer == null) { + throw new DataValidationException("Can't assign entity view to non-existent customer!"); + } + if (!customer.getTenantId().getId().equals(entityView.getTenantId().getId())) { + throw new DataValidationException("Can't assign entity view to customer from different tenant!"); + } + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/EventDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/EventDataValidator.java new file mode 100644 index 0000000000..ae7b7542a4 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/EventDataValidator.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.Event; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; + +@Component +public class EventDataValidator extends DataValidator { + + @Override + protected void validateDataImpl(TenantId tenantId, Event event) { + if (event.getEntityId() == null) { + throw new DataValidationException("Entity id should be specified!."); + } + if (StringUtils.isEmpty(event.getType())) { + throw new DataValidationException("Event type should be specified!."); + } + if (event.getBody() == null) { + throw new DataValidationException("Event body should be specified!."); + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/OtaPackageDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/OtaPackageDataValidator.java new file mode 100644 index 0000000000..8b261ad226 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/OtaPackageDataValidator.java @@ -0,0 +1,105 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.ota.OtaPackageDao; +import org.thingsboard.server.dao.ota.OtaPackageService; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; + +import static org.thingsboard.server.common.data.EntityType.OTA_PACKAGE; + +@Component +public class OtaPackageDataValidator extends BaseOtaPackageDataValidator { + + @Autowired + private OtaPackageDao otaPackageDao; + + @Autowired + @Lazy + private OtaPackageService otaPackageService; + + @Autowired + @Lazy + private TbTenantProfileCache tenantProfileCache; + + @Override + protected void validateCreate(TenantId tenantId, OtaPackage otaPackage) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxOtaPackagesInBytes = profileConfiguration.getMaxOtaPackagesInBytes(); + validateMaxSumDataSizePerTenant(tenantId, otaPackageDao, maxOtaPackagesInBytes, otaPackage.getDataSize(), OTA_PACKAGE); + } + + @Override + protected void validateDataImpl(TenantId tenantId, OtaPackage otaPackage) { + validateImpl(otaPackage); + + if (!otaPackage.hasUrl()) { + if (StringUtils.isEmpty(otaPackage.getFileName())) { + throw new DataValidationException("OtaPackage file name should be specified!"); + } + + if (StringUtils.isEmpty(otaPackage.getContentType())) { + throw new DataValidationException("OtaPackage content type should be specified!"); + } + + if (otaPackage.getChecksumAlgorithm() == null) { + throw new DataValidationException("OtaPackage checksum algorithm should be specified!"); + } + if (StringUtils.isEmpty(otaPackage.getChecksum())) { + throw new DataValidationException("OtaPackage checksum should be specified!"); + } + + String currentChecksum; + + currentChecksum = otaPackageService.generateChecksum(otaPackage.getChecksumAlgorithm(), otaPackage.getData()); + + if (!currentChecksum.equals(otaPackage.getChecksum())) { + throw new DataValidationException("Wrong otaPackage file!"); + } + } else { + if (otaPackage.getData() != null) { + throw new DataValidationException("File can't be saved if URL present!"); + } + } + } + + @Override + protected void validateUpdate(TenantId tenantId, OtaPackage otaPackage) { + OtaPackage otaPackageOld = otaPackageDao.findById(tenantId, otaPackage.getUuidId()); + + validateUpdate(otaPackage, otaPackageOld); + + if (otaPackageOld.getData() != null && !otaPackageOld.getData().equals(otaPackage.getData())) { + throw new DataValidationException("Updating otaPackage data is prohibited!"); + } + + if (otaPackageOld.getData() == null && otaPackage.getData() != null) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxOtaPackagesInBytes = profileConfiguration.getMaxOtaPackagesInBytes(); + validateMaxSumDataSizePerTenant(tenantId, otaPackageDao, maxOtaPackagesInBytes, otaPackage.getDataSize(), OTA_PACKAGE); + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/OtaPackageInfoDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/OtaPackageInfoDataValidator.java new file mode 100644 index 0000000000..d880dfe42c --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/OtaPackageInfoDataValidator.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.ota.OtaPackageInfoDao; + +@Component +public class OtaPackageInfoDataValidator extends BaseOtaPackageDataValidator { + + @Autowired + private OtaPackageInfoDao otaPackageInfoDao; + + @Override + protected void validateDataImpl(TenantId tenantId, OtaPackageInfo otaPackageInfo) { + validateImpl(otaPackageInfo); + } + + @Override + protected void validateUpdate(TenantId tenantId, OtaPackageInfo otaPackage) { + OtaPackageInfo otaPackageOld = otaPackageInfoDao.findById(tenantId, otaPackage.getUuidId()); + validateUpdate(otaPackage, otaPackageOld); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java new file mode 100644 index 0000000000..90ac54577a --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java @@ -0,0 +1,82 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.resource.TbResourceDao; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; +import org.thingsboard.server.dao.tenant.TenantDao; + +import static org.thingsboard.server.common.data.EntityType.TB_RESOURCE; + +@Component +public class ResourceDataValidator extends DataValidator { + + @Autowired + private TbResourceDao resourceDao; + + @Autowired + private TenantDao tenantDao; + + @Autowired + @Lazy + private TbTenantProfileCache tenantProfileCache; + + @Override + protected void validateCreate(TenantId tenantId, TbResource resource) { + if (tenantId != null && !TenantId.SYS_TENANT_ID.equals(tenantId)) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxSumResourcesDataInBytes = profileConfiguration.getMaxResourcesInBytes(); + validateMaxSumDataSizePerTenant(tenantId, resourceDao, maxSumResourcesDataInBytes, resource.getData().length(), TB_RESOURCE); + } + } + + @Override + protected void validateDataImpl(TenantId tenantId, TbResource resource) { + if (StringUtils.isEmpty(resource.getTitle())) { + throw new DataValidationException("Resource title should be specified!"); + } + if (resource.getResourceType() == null) { + throw new DataValidationException("Resource type should be specified!"); + } + if (StringUtils.isEmpty(resource.getFileName())) { + throw new DataValidationException("Resource file name should be specified!"); + } + if (StringUtils.isEmpty(resource.getResourceKey())) { + throw new DataValidationException("Resource key should be specified!"); + } + if (resource.getTenantId() == null) { + resource.setTenantId(TenantId.fromUUID(ModelConstants.NULL_UUID)); + } + if (!resource.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { + Tenant tenant = tenantDao.findById(tenantId, resource.getTenantId().getId()); + if (tenant == null) { + throw new DataValidationException("Resource is referencing to non-existent tenant!"); + } + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/RuleChainDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/RuleChainDataValidator.java new file mode 100644 index 0000000000..f36b4d23fa --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/RuleChainDataValidator.java @@ -0,0 +1,88 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainType; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.rule.RuleChainDao; +import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; +import org.thingsboard.server.dao.tenant.TenantDao; + +@Component +public class RuleChainDataValidator extends DataValidator { + + @Autowired + private RuleChainDao ruleChainDao; + + @Autowired + @Lazy + private RuleChainService ruleChainService; + + @Autowired + private TenantDao tenantDao; + + @Autowired + @Lazy + private TbTenantProfileCache tenantProfileCache; + + @Override + protected void validateCreate(TenantId tenantId, RuleChain data) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxRuleChains = profileConfiguration.getMaxRuleChains(); + validateNumberOfEntitiesPerTenant(tenantId, ruleChainDao, maxRuleChains, EntityType.RULE_CHAIN); + } + + @Override + protected void validateDataImpl(TenantId tenantId, RuleChain ruleChain) { + if (StringUtils.isEmpty(ruleChain.getName())) { + throw new DataValidationException("Rule chain name should be specified!"); + } + if (ruleChain.getType() == null) { + ruleChain.setType(RuleChainType.CORE); + } + if (ruleChain.getTenantId() == null || ruleChain.getTenantId().isNullUid()) { + throw new DataValidationException("Rule chain should be assigned to tenant!"); + } + Tenant tenant = tenantDao.findById(tenantId, ruleChain.getTenantId().getId()); + if (tenant == null) { + throw new DataValidationException("Rule chain is referencing to non-existent tenant!"); + } + if (ruleChain.isRoot() && RuleChainType.CORE.equals(ruleChain.getType())) { + RuleChain rootRuleChain = ruleChainService.getRootTenantRuleChain(ruleChain.getTenantId()); + if (rootRuleChain != null && !rootRuleChain.getId().equals(ruleChain.getId())) { + throw new DataValidationException("Another root rule chain is present in scope of current tenant!"); + } + } + if (ruleChain.isRoot() && RuleChainType.EDGE.equals(ruleChain.getType())) { + RuleChain edgeTemplateRootRuleChain = ruleChainService.getEdgeTemplateRootRuleChain(ruleChain.getTenantId()); + if (edgeTemplateRootRuleChain != null && !edgeTemplateRootRuleChain.getId().equals(ruleChain.getId())) { + throw new DataValidationException("Another edge template root rule chain is present in scope of current tenant!"); + } + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantDataValidator.java new file mode 100644 index 0000000000..2c5f6b655e --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantDataValidator.java @@ -0,0 +1,68 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TenantDao; +import org.thingsboard.server.dao.tenant.TenantProfileService; + +@Component +public class TenantDataValidator extends DataValidator { + + @Value("${zk.enabled}") + private Boolean zkEnabled; + + @Autowired + private TenantProfileService tenantProfileService; + + @Autowired + private TenantDao tenantDao; + + @Override + protected void validateDataImpl(TenantId tenantId, Tenant tenant) { + if (StringUtils.isEmpty(tenant.getTitle())) { + throw new DataValidationException("Tenant title should be specified!"); + } + if (!StringUtils.isEmpty(tenant.getEmail())) { + validateEmail(tenant.getEmail()); + } + validateTenantProfile(tenantId, tenant); + } + + @Override + protected void validateUpdate(TenantId tenantId, Tenant tenant) { + Tenant old = tenantDao.findById(TenantId.SYS_TENANT_ID, tenantId.getId()); + if (old == null) { + throw new DataValidationException("Can't update non existing tenant!"); + } + validateTenantProfile(tenantId, tenant); + } + + private void validateTenantProfile(TenantId tenantId, Tenant tenant) { + TenantProfile tenantProfileById = tenantProfileService.findTenantProfileById(tenantId, tenant.getTenantProfileId()); + if (!zkEnabled && (tenantProfileById.isIsolatedTbCore() || tenantProfileById.isIsolatedTbRuleEngine())) { + throw new DataValidationException("Can't use isolated tenant profiles in monolith setup!"); + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java new file mode 100644 index 0000000000..e8207534be --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java @@ -0,0 +1,69 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TenantProfileDao; +import org.thingsboard.server.dao.tenant.TenantProfileService; + +@Component +public class TenantProfileDataValidator extends DataValidator { + + @Autowired + private TenantProfileDao tenantProfileDao; + + @Autowired + @Lazy + private TenantProfileService tenantProfileService; + + @Override + protected void validateDataImpl(TenantId tenantId, TenantProfile tenantProfile) { + if (StringUtils.isEmpty(tenantProfile.getName())) { + throw new DataValidationException("Tenant profile name should be specified!"); + } + if (tenantProfile.getProfileData() == null) { + throw new DataValidationException("Tenant profile data should be specified!"); + } + if (tenantProfile.getProfileData().getConfiguration() == null) { + throw new DataValidationException("Tenant profile data configuration should be specified!"); + } + if (tenantProfile.isDefault()) { + TenantProfile defaultTenantProfile = tenantProfileService.findDefaultTenantProfile(tenantId); + if (defaultTenantProfile != null && !defaultTenantProfile.getId().equals(tenantProfile.getId())) { + throw new DataValidationException("Another default tenant profile is present!"); + } + } + } + + @Override + protected void validateUpdate(TenantId tenantId, TenantProfile tenantProfile) { + TenantProfile old = tenantProfileDao.findById(TenantId.SYS_TENANT_ID, tenantProfile.getId().getId()); + if (old == null) { + throw new DataValidationException("Can't update non existing tenant profile!"); + } else if (old.isIsolatedTbRuleEngine() != tenantProfile.isIsolatedTbRuleEngine()) { + throw new DataValidationException("Can't update isolatedTbRuleEngine property!"); + } else if (old.isIsolatedTbCore() != tenantProfile.isIsolatedTbCore()) { + throw new DataValidationException("Can't update isolatedTbCore property!"); + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/UserCredentialsDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/UserCredentialsDataValidator.java new file mode 100644 index 0000000000..5920ad552f --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/UserCredentialsDataValidator.java @@ -0,0 +1,68 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.security.UserCredentials; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.exception.IncorrectParameterException; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.user.UserCredentialsDao; +import org.thingsboard.server.dao.user.UserService; + +@Component +public class UserCredentialsDataValidator extends DataValidator { + + @Autowired + private UserCredentialsDao userCredentialsDao; + + @Autowired + @Lazy + private UserService userService; + + @Override + protected void validateCreate(TenantId tenantId, UserCredentials userCredentials) { + throw new IncorrectParameterException("Creation of new user credentials is prohibited."); + } + + @Override + protected void validateDataImpl(TenantId tenantId, UserCredentials userCredentials) { + if (userCredentials.getUserId() == null) { + throw new DataValidationException("User credentials should be assigned to user!"); + } + if (userCredentials.isEnabled()) { + if (StringUtils.isEmpty(userCredentials.getPassword())) { + throw new DataValidationException("Enabled user credentials should have password!"); + } + if (StringUtils.isNotEmpty(userCredentials.getActivateToken())) { + throw new DataValidationException("Enabled user credentials can't have activate token!"); + } + } + UserCredentials existingUserCredentialsEntity = userCredentialsDao.findById(tenantId, userCredentials.getId().getId()); + if (existingUserCredentialsEntity == null) { + throw new DataValidationException("Unable to update non-existent user credentials!"); + } + User user = userService.findUserById(tenantId, userCredentials.getUserId()); + if (user == null) { + throw new DataValidationException("Can't assign user credentials to non-existent user!"); + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/UserDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/UserDataValidator.java new file mode 100644 index 0000000000..94b894acce --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/UserDataValidator.java @@ -0,0 +1,136 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.dao.customer.CustomerDao; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; +import org.thingsboard.server.dao.tenant.TenantDao; +import org.thingsboard.server.dao.user.UserDao; +import org.thingsboard.server.dao.user.UserService; + +@Component +public class UserDataValidator extends DataValidator { + + @Autowired + private UserDao userDao; + + @Autowired + @Lazy + private UserService userService; + + @Autowired + private TenantDao tenantDao; + + @Autowired + private CustomerDao customerDao; + + @Autowired + @Lazy + private TbTenantProfileCache tenantProfileCache; + + @Override + protected void validateCreate(TenantId tenantId, User user) { + if (!user.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxUsers = profileConfiguration.getMaxUsers(); + validateNumberOfEntitiesPerTenant(tenantId, userDao, maxUsers, EntityType.USER); + } + } + + @Override + protected void validateDataImpl(TenantId requestTenantId, User user) { + if (StringUtils.isEmpty(user.getEmail())) { + throw new DataValidationException("User email should be specified!"); + } + + validateEmail(user.getEmail()); + + Authority authority = user.getAuthority(); + if (authority == null) { + throw new DataValidationException("User authority isn't defined!"); + } + TenantId tenantId = user.getTenantId(); + if (tenantId == null) { + tenantId = TenantId.fromUUID(ModelConstants.NULL_UUID); + user.setTenantId(tenantId); + } + CustomerId customerId = user.getCustomerId(); + if (customerId == null) { + customerId = new CustomerId(ModelConstants.NULL_UUID); + user.setCustomerId(customerId); + } + + switch (authority) { + case SYS_ADMIN: + if (!tenantId.getId().equals(ModelConstants.NULL_UUID) + || !customerId.getId().equals(ModelConstants.NULL_UUID)) { + throw new DataValidationException("System administrator can't be assigned neither to tenant nor to customer!"); + } + break; + case TENANT_ADMIN: + if (tenantId.getId().equals(ModelConstants.NULL_UUID)) { + throw new DataValidationException("Tenant administrator should be assigned to tenant!"); + } else if (!customerId.getId().equals(ModelConstants.NULL_UUID)) { + throw new DataValidationException("Tenant administrator can't be assigned to customer!"); + } + break; + case CUSTOMER_USER: + if (tenantId.getId().equals(ModelConstants.NULL_UUID) + || customerId.getId().equals(ModelConstants.NULL_UUID)) { + throw new DataValidationException("Customer user should be assigned to customer!"); + } + break; + default: + break; + } + + User existentUserWithEmail = userService.findUserByEmail(tenantId, user.getEmail()); + if (existentUserWithEmail != null && !isSameData(existentUserWithEmail, user)) { + throw new DataValidationException("User with email '" + user.getEmail() + "' " + + " already present in database!"); + } + if (!tenantId.getId().equals(ModelConstants.NULL_UUID)) { + Tenant tenant = tenantDao.findById(tenantId, user.getTenantId().getId()); + if (tenant == null) { + throw new DataValidationException("User is referencing to non-existent tenant!"); + } + } + if (!customerId.getId().equals(ModelConstants.NULL_UUID)) { + Customer customer = customerDao.findById(tenantId, user.getCustomerId().getId()); + if (customer == null) { + throw new DataValidationException("User is referencing to non-existent customer!"); + } else if (!customer.getTenantId().getId().equals(tenantId.getId())) { + throw new DataValidationException("User can't be assigned to customer from different tenant!"); + } + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetTypeDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetTypeDataValidator.java new file mode 100644 index 0000000000..ec3be14866 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetTypeDataValidator.java @@ -0,0 +1,98 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import lombok.AllArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.widget.WidgetType; +import org.thingsboard.server.common.data.widget.WidgetTypeDetails; +import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TenantDao; +import org.thingsboard.server.dao.widget.WidgetTypeDao; +import org.thingsboard.server.dao.widget.WidgetsBundleDao; + +@Component +@AllArgsConstructor +public class WidgetTypeDataValidator extends DataValidator { + + private final WidgetTypeDao widgetTypeDao; + private final TenantDao tenantDao; + private final WidgetsBundleDao widgetsBundleDao; + + @Override + protected void validateDataImpl(TenantId tenantId, WidgetTypeDetails widgetTypeDetails) { + if (StringUtils.isEmpty(widgetTypeDetails.getName())) { + throw new DataValidationException("Widgets type name should be specified!"); + } + if (StringUtils.isEmpty(widgetTypeDetails.getBundleAlias())) { + throw new DataValidationException("Widgets type bundle alias should be specified!"); + } + if (widgetTypeDetails.getDescriptor() == null || widgetTypeDetails.getDescriptor().size() == 0) { + throw new DataValidationException("Widgets type descriptor can't be empty!"); + } + if (widgetTypeDetails.getTenantId() == null) { + widgetTypeDetails.setTenantId(TenantId.fromUUID(ModelConstants.NULL_UUID)); + } + if (!widgetTypeDetails.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { + Tenant tenant = tenantDao.findById(tenantId, widgetTypeDetails.getTenantId().getId()); + if (tenant == null) { + throw new DataValidationException("Widget type is referencing to non-existent tenant!"); + } + } + } + + @Override + protected void validateCreate(TenantId tenantId, WidgetTypeDetails widgetTypeDetails) { + WidgetsBundle widgetsBundle = widgetsBundleDao.findWidgetsBundleByTenantIdAndAlias(widgetTypeDetails.getTenantId().getId(), widgetTypeDetails.getBundleAlias()); + if (widgetsBundle == null) { + throw new DataValidationException("Widget type is referencing to non-existent widgets bundle!"); + } + String alias = widgetTypeDetails.getAlias(); + if (alias == null || alias.trim().isEmpty()) { + alias = widgetTypeDetails.getName().toLowerCase().replaceAll("\\W+", "_"); + } + String originalAlias = alias; + int c = 1; + WidgetType withSameAlias; + do { + withSameAlias = widgetTypeDao.findByTenantIdBundleAliasAndAlias(widgetTypeDetails.getTenantId().getId(), widgetTypeDetails.getBundleAlias(), alias); + if (withSameAlias != null) { + alias = originalAlias + (++c); + } + } while (withSameAlias != null); + widgetTypeDetails.setAlias(alias); + } + + @Override + protected void validateUpdate(TenantId tenantId, WidgetTypeDetails widgetTypeDetails) { + WidgetType storedWidgetType = widgetTypeDao.findById(tenantId, widgetTypeDetails.getId().getId()); + if (!storedWidgetType.getTenantId().getId().equals(widgetTypeDetails.getTenantId().getId())) { + throw new DataValidationException("Can't move existing widget type to different tenant!"); + } + if (!storedWidgetType.getBundleAlias().equals(widgetTypeDetails.getBundleAlias())) { + throw new DataValidationException("Update of widget type bundle alias is prohibited!"); + } + if (!storedWidgetType.getAlias().equals(widgetTypeDetails.getAlias())) { + throw new DataValidationException("Update of widget type alias is prohibited!"); + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetsBundleDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetsBundleDataValidator.java new file mode 100644 index 0000000000..d5b1a1149c --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetsBundleDataValidator.java @@ -0,0 +1,81 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import lombok.AllArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TenantDao; +import org.thingsboard.server.dao.widget.WidgetsBundleDao; + +@Component +@AllArgsConstructor +public class WidgetsBundleDataValidator extends DataValidator { + + private final WidgetsBundleDao widgetsBundleDao; + private final TenantDao tenantDao; + + @Override + protected void validateDataImpl(TenantId tenantId, WidgetsBundle widgetsBundle) { + if (StringUtils.isEmpty(widgetsBundle.getTitle())) { + throw new DataValidationException("Widgets bundle title should be specified!"); + } + if (widgetsBundle.getTenantId() == null) { + widgetsBundle.setTenantId(TenantId.fromUUID(ModelConstants.NULL_UUID)); + } + if (!widgetsBundle.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { + Tenant tenant = tenantDao.findById(tenantId, widgetsBundle.getTenantId().getId()); + if (tenant == null) { + throw new DataValidationException("Widgets bundle is referencing to non-existent tenant!"); + } + } + } + + @Override + protected void validateCreate(TenantId tenantId, WidgetsBundle widgetsBundle) { + String alias = widgetsBundle.getAlias(); + if (alias == null || alias.trim().isEmpty()) { + alias = widgetsBundle.getTitle().toLowerCase().replaceAll("\\W+", "_"); + } + String originalAlias = alias; + int c = 1; + WidgetsBundle withSameAlias; + do { + withSameAlias = widgetsBundleDao.findWidgetsBundleByTenantIdAndAlias(widgetsBundle.getTenantId().getId(), alias); + if (withSameAlias != null) { + alias = originalAlias + (++c); + } + } while (withSameAlias != null); + widgetsBundle.setAlias(alias); + } + + @Override + protected void validateUpdate(TenantId tenantId, WidgetsBundle widgetsBundle) { + WidgetsBundle storedWidgetsBundle = widgetsBundleDao.findById(tenantId, widgetsBundle.getId().getId()); + if (!storedWidgetsBundle.getTenantId().getId().equals(widgetsBundle.getTenantId().getId())) { + throw new DataValidationException("Can't move existing widgets bundle to different tenant!"); + } + if (!storedWidgetsBundle.getAlias().equals(widgetsBundle.getAlias())) { + throw new DataValidationException("Update of widgets bundle alias is prohibited!"); + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java index fdca0178f5..bf4c71d296 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java @@ -17,13 +17,11 @@ package org.thingsboard.server.dao.settings; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.id.AdminSettingsId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.Validator; @@ -34,6 +32,9 @@ public class AdminSettingsServiceImpl implements AdminSettingsService { @Autowired private AdminSettingsDao adminSettingsDao; + @Autowired + private DataValidator adminSettingsValidator; + @Override public AdminSettings findAdminSettingsById(TenantId tenantId, AdminSettingsId adminSettingsId) { log.trace("Executing findAdminSettingsById [{}]", adminSettingsId); @@ -62,37 +63,4 @@ public class AdminSettingsServiceImpl implements AdminSettingsService { return adminSettingsDao.save(tenantId, adminSettings); } - private DataValidator adminSettingsValidator = - new DataValidator() { - - @Override - protected void validateCreate(TenantId tenantId, AdminSettings adminSettings) { - AdminSettings existentAdminSettingsWithKey = findAdminSettingsByKey(tenantId, adminSettings.getKey()); - if (existentAdminSettingsWithKey != null) { - throw new DataValidationException("Admin settings with such name already exists!"); - } - } - - @Override - protected void validateUpdate(TenantId tenantId, AdminSettings adminSettings) { - AdminSettings existentAdminSettings = findAdminSettingsById(tenantId, adminSettings.getId()); - if (existentAdminSettings != null) { - if (!existentAdminSettings.getKey().equals(adminSettings.getKey())) { - throw new DataValidationException("Changing key of admin settings entry is prohibited!"); - } - } - } - - - @Override - protected void validateDataImpl(TenantId tenantId, AdminSettings adminSettings) { - if (StringUtils.isEmpty(adminSettings.getKey())) { - throw new DataValidationException("Key should be specified!"); - } - if (adminSettings.getJsonValue() == null) { - throw new DataValidationException("Json value should be specified!"); - } - } - }; - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java index a4f9c65807..0976c49410 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java @@ -16,7 +16,6 @@ package org.thingsboard.server.dao.tenant; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.hibernate.exception.ConstraintViolationException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; @@ -55,6 +54,9 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T @Autowired private CacheManager cacheManager; + @Autowired + private DataValidator tenantProfileValidator; + @Cacheable(cacheNames = TENANT_PROFILE_CACHE, key = "{#tenantProfileId.id}") @Override public TenantProfile findTenantProfileById(TenantId tenantId, TenantProfileId tenantProfileId) { @@ -104,10 +106,10 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T if (tenantProfile != null && tenantProfile.isDefault()) { throw new DataValidationException("Deletion of Default Tenant Profile is prohibited!"); } - this.removeTenantProfile(tenantId, tenantProfileId); + this.removeTenantProfile(tenantId, tenantProfileId, false); } - private void removeTenantProfile(TenantId tenantId, TenantProfileId tenantProfileId) { + private void removeTenantProfile(TenantId tenantId, TenantProfileId tenantProfileId, boolean isDefault) { try { tenantProfileDao.removeById(tenantId, tenantProfileId.getId()); } catch (Exception t) { @@ -122,6 +124,10 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T Cache cache = cacheManager.getCache(TENANT_PROFILE_CACHE); cache.evict(Collections.singletonList(tenantProfileId.getId())); cache.evict(Arrays.asList("info", tenantProfileId.getId())); + if (isDefault) { + cache.evict(Collections.singletonList("default")); + cache.evict(Arrays.asList("default", "info")); + } } @Override @@ -209,41 +215,7 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T tenantProfilesRemover.removeEntities(tenantId, null); } - private DataValidator tenantProfileValidator = - new DataValidator() { - @Override - protected void validateDataImpl(TenantId tenantId, TenantProfile tenantProfile) { - if (StringUtils.isEmpty(tenantProfile.getName())) { - throw new DataValidationException("Tenant profile name should be specified!"); - } - if (tenantProfile.getProfileData() == null) { - throw new DataValidationException("Tenant profile data should be specified!"); - } - if (tenantProfile.getProfileData().getConfiguration() == null) { - throw new DataValidationException("Tenant profile data configuration should be specified!"); - } - if (tenantProfile.isDefault()) { - TenantProfile defaultTenantProfile = findDefaultTenantProfile(tenantId); - if (defaultTenantProfile != null && !defaultTenantProfile.getId().equals(tenantProfile.getId())) { - throw new DataValidationException("Another default tenant profile is present!"); - } - } - } - - @Override - protected void validateUpdate(TenantId tenantId, TenantProfile tenantProfile) { - TenantProfile old = tenantProfileDao.findById(TenantId.SYS_TENANT_ID, tenantProfile.getId().getId()); - if (old == null) { - throw new DataValidationException("Can't update non existing tenant profile!"); - } else if (old.isIsolatedTbRuleEngine() != tenantProfile.isIsolatedTbRuleEngine()) { - throw new DataValidationException("Can't update isolatedTbRuleEngine property!"); - } else if (old.isIsolatedTbCore() != tenantProfile.isIsolatedTbCore()) { - throw new DataValidationException("Can't update isolatedTbCore property!"); - } - } - }; - - private PaginatedRemover tenantProfilesRemover = + private final PaginatedRemover tenantProfilesRemover = new PaginatedRemover() { @Override @@ -253,7 +225,7 @@ public class TenantProfileServiceImpl extends AbstractEntityService implements T @Override protected void removeEntity(TenantId tenantId, TenantProfile entity) { - removeTenantProfile(tenantId, entity.getId()); + removeTenantProfile(tenantId, entity.getId(), entity.isDefault()); } }; diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index a8a1dc6e5b..6488c898da 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -17,9 +17,7 @@ package org.thingsboard.server.dao.tenant; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantInfo; @@ -34,7 +32,6 @@ import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entityview.EntityViewService; -import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rpc.RpcService; @@ -55,9 +52,6 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe private static final String DEFAULT_TENANT_REGION = "Global"; public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; - @Value("${zk.enabled}") - private Boolean zkEnabled; - @Autowired private TenantDao tenantDao; @@ -103,6 +97,9 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe @Autowired private RpcService rpcService; + @Autowired + private DataValidator tenantValidator; + @Override public Tenant findTenantById(TenantId tenantId) { log.trace("Executing findTenantById [{}]", tenantId); @@ -183,36 +180,6 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe tenantsRemover.removeEntities(TenantId.SYS_TENANT_ID, TenantId.SYS_TENANT_ID); } - private DataValidator tenantValidator = - new DataValidator() { - @Override - protected void validateDataImpl(TenantId tenantId, Tenant tenant) { - if (StringUtils.isEmpty(tenant.getTitle())) { - throw new DataValidationException("Tenant title should be specified!"); - } - if (!StringUtils.isEmpty(tenant.getEmail())) { - validateEmail(tenant.getEmail()); - } - validateTenantProfile(tenantId, tenant); - } - - @Override - protected void validateUpdate(TenantId tenantId, Tenant tenant) { - Tenant old = tenantDao.findById(TenantId.SYS_TENANT_ID, tenantId.getId()); - if (old == null) { - throw new DataValidationException("Can't update non existing tenant!"); - } - validateTenantProfile(tenantId, tenant); - } - - private void validateTenantProfile(TenantId tenantId, Tenant tenant) { - TenantProfile tenantProfileById = tenantProfileService.findTenantProfileById(tenantId, tenant.getTenantProfileId()); - if (!zkEnabled && (tenantProfileById.isIsolatedTbCore() || tenantProfileById.isIsolatedTbRuleEngine())) { - throw new DataValidationException("Can't use isolated tenant profiles in monolith setup!"); - } - } - }; - private PaginatedRemover tenantsRemover = new PaginatedRemover<>() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java index 5b75342420..2193305837 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.usagerecord; +import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.ApiFeature; @@ -33,7 +34,6 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.tenant.profile.TenantProfileConfiguration; import org.thingsboard.server.dao.entity.AbstractEntityService; -import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantDao; import org.thingsboard.server.dao.tenant.TenantProfileDao; @@ -47,6 +47,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @Service @Slf4j +@AllArgsConstructor public class ApiUsageStateServiceImpl extends AbstractEntityService implements ApiUsageStateService { public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; @@ -54,13 +55,7 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A private final TenantProfileDao tenantProfileDao; private final TenantDao tenantDao; private final TimeseriesService tsService; - - public ApiUsageStateServiceImpl(TenantDao tenantDao, ApiUsageStateDao apiUsageStateDao, TenantProfileDao tenantProfileDao, TimeseriesService tsService) { - this.tenantDao = tenantDao; - this.apiUsageStateDao = apiUsageStateDao; - this.tenantProfileDao = tenantProfileDao; - this.tsService = tsService; - } + private final DataValidator apiUsageStateValidator; @Override public void deleteApiUsageStateByTenantId(TenantId tenantId) { @@ -157,24 +152,4 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A return apiUsageStateDao.findById(tenantId, id.getId()); } - private DataValidator apiUsageStateValidator = - new DataValidator() { - @Override - protected void validateDataImpl(TenantId requestTenantId, ApiUsageState apiUsageState) { - if (apiUsageState.getTenantId() == null) { - throw new DataValidationException("ApiUsageState should be assigned to tenant!"); - } else { - Tenant tenant = tenantDao.findById(requestTenantId, apiUsageState.getTenantId().getId()); - if (tenant == null && !requestTenantId.equals(TenantId.SYS_TENANT_ID)) { - throw new DataValidationException("ApiUsageState is referencing to non-existent tenant!"); - } - } - if (apiUsageState.getEntityId() == null) { - throw new DataValidationException("UsageRecord should be assigned to entity!"); - } else if (apiUsageState.getEntityId().getEntityType() != EntityType.TENANT && apiUsageState.getEntityId().getEntityType() != EntityType.CUSTOMER) { - throw new DataValidationException("Only Tenant and Customer Usage Records are supported!"); - } - } - }; - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index d48520648e..ba0f09d64e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -19,19 +19,15 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.util.concurrent.ListenableFuture; +import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomStringUtils; -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.annotation.Lazy; import org.springframework.security.authentication.DisabledException; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.Customer; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; @@ -39,19 +35,12 @@ import org.thingsboard.server.common.data.id.UserCredentialsId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.security.event.UserAuthDataChangedEvent; -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; -import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.entity.AbstractEntityService; -import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; -import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; -import org.thingsboard.server.dao.tenant.TbTenantProfileCache; -import org.thingsboard.server.dao.tenant.TenantDao; import java.util.HashMap; import java.util.Map; @@ -80,22 +69,19 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic private final UserDao userDao; private final UserCredentialsDao userCredentialsDao; - private final TenantDao tenantDao; - private final CustomerDao customerDao; - private final TbTenantProfileCache tenantProfileCache; + private final DataValidator userValidator; + private final DataValidator userCredentialsValidator; private final ApplicationEventPublisher eventPublisher; public UserServiceImpl(UserDao userDao, UserCredentialsDao userCredentialsDao, - TenantDao tenantDao, - CustomerDao customerDao, - @Lazy TbTenantProfileCache tenantProfileCache, + DataValidator userValidator, + DataValidator userCredentialsValidator, ApplicationEventPublisher eventPublisher) { this.userDao = userDao; this.userCredentialsDao = userCredentialsDao; - this.tenantDao = tenantDao; - this.customerDao = customerDao; - this.tenantProfileCache = tenantProfileCache; + this.userValidator = userValidator; + this.userCredentialsValidator = userCredentialsValidator; this.eventPublisher = eventPublisher; } @@ -383,119 +369,6 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic saveUser(user); } - private final DataValidator userValidator = - new DataValidator<>() { - @Override - protected void validateCreate(TenantId tenantId, User user) { - if (!user.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { - DefaultTenantProfileConfiguration profileConfiguration = - (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); - long maxUsers = profileConfiguration.getMaxUsers(); - validateNumberOfEntitiesPerTenant(tenantId, userDao, maxUsers, EntityType.USER); - } - } - - @Override - protected void validateDataImpl(TenantId requestTenantId, User user) { - if (StringUtils.isEmpty(user.getEmail())) { - throw new DataValidationException("User email should be specified!"); - } - - validateEmail(user.getEmail()); - - Authority authority = user.getAuthority(); - if (authority == null) { - throw new DataValidationException("User authority isn't defined!"); - } - TenantId tenantId = user.getTenantId(); - if (tenantId == null) { - tenantId = TenantId.fromUUID(ModelConstants.NULL_UUID); - user.setTenantId(tenantId); - } - CustomerId customerId = user.getCustomerId(); - if (customerId == null) { - customerId = new CustomerId(ModelConstants.NULL_UUID); - user.setCustomerId(customerId); - } - - switch (authority) { - case SYS_ADMIN: - if (!tenantId.getId().equals(ModelConstants.NULL_UUID) - || !customerId.getId().equals(ModelConstants.NULL_UUID)) { - throw new DataValidationException("System administrator can't be assigned neither to tenant nor to customer!"); - } - break; - case TENANT_ADMIN: - if (tenantId.getId().equals(ModelConstants.NULL_UUID)) { - throw new DataValidationException("Tenant administrator should be assigned to tenant!"); - } else if (!customerId.getId().equals(ModelConstants.NULL_UUID)) { - throw new DataValidationException("Tenant administrator can't be assigned to customer!"); - } - break; - case CUSTOMER_USER: - if (tenantId.getId().equals(ModelConstants.NULL_UUID) - || customerId.getId().equals(ModelConstants.NULL_UUID)) { - throw new DataValidationException("Customer user should be assigned to customer!"); - } - break; - default: - break; - } - - User existentUserWithEmail = findUserByEmail(tenantId, user.getEmail()); - if (existentUserWithEmail != null && !isSameData(existentUserWithEmail, user)) { - throw new DataValidationException("User with email '" + user.getEmail() + "' " - + " already present in database!"); - } - if (!tenantId.getId().equals(ModelConstants.NULL_UUID)) { - Tenant tenant = tenantDao.findById(tenantId, user.getTenantId().getId()); - if (tenant == null) { - throw new DataValidationException("User is referencing to non-existent tenant!"); - } - } - if (!customerId.getId().equals(ModelConstants.NULL_UUID)) { - Customer customer = customerDao.findById(tenantId, user.getCustomerId().getId()); - if (customer == null) { - throw new DataValidationException("User is referencing to non-existent customer!"); - } else if (!customer.getTenantId().getId().equals(tenantId.getId())) { - throw new DataValidationException("User can't be assigned to customer from different tenant!"); - } - } - } - }; - - private final DataValidator userCredentialsValidator = - new DataValidator<>() { - - @Override - protected void validateCreate(TenantId tenantId, UserCredentials userCredentials) { - throw new IncorrectParameterException("Creation of new user credentials is prohibited."); - } - - @Override - protected void validateDataImpl(TenantId tenantId, UserCredentials userCredentials) { - if (userCredentials.getUserId() == null) { - throw new DataValidationException("User credentials should be assigned to user!"); - } - if (userCredentials.isEnabled()) { - if (StringUtils.isEmpty(userCredentials.getPassword())) { - throw new DataValidationException("Enabled user credentials should have password!"); - } - if (StringUtils.isNotEmpty(userCredentials.getActivateToken())) { - throw new DataValidationException("Enabled user credentials can't have activate token!"); - } - } - UserCredentials existingUserCredentialsEntity = userCredentialsDao.findById(tenantId, userCredentials.getId().getId()); - if (existingUserCredentialsEntity == null) { - throw new DataValidationException("Unable to update non-existent user credentials!"); - } - User user = findUserById(tenantId, userCredentials.getUserId()); - if (user == null) { - throw new DataValidationException("Can't assign user credentials to non-existent user!"); - } - } - }; - private final PaginatedRemover tenantAdminsRemover = new PaginatedRemover<>() { @Override protected PageData findEntities(TenantId tenantId, TenantId id, PageLink pageLink) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java index 4defd1b34d..9b64dc0d68 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java @@ -16,21 +16,15 @@ package org.thingsboard.server.dao.widget; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetTypeId; import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetTypeInfo; -import org.thingsboard.server.common.data.widget.WidgetsBundle; -import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.Validator; -import org.thingsboard.server.dao.tenant.TenantDao; import java.util.List; @@ -44,10 +38,7 @@ public class WidgetTypeServiceImpl implements WidgetTypeService { private WidgetTypeDao widgetTypeDao; @Autowired - private TenantDao tenantDao; - - @Autowired - private WidgetsBundleDao widgetsBundleService; + private DataValidator widgetTypeValidator; @Override public WidgetType findWidgetTypeById(TenantId tenantId, WidgetTypeId widgetTypeId) { @@ -121,64 +112,4 @@ public class WidgetTypeServiceImpl implements WidgetTypeService { } } - private DataValidator widgetTypeValidator = - new DataValidator<>() { - @Override - protected void validateDataImpl(TenantId tenantId, WidgetTypeDetails widgetTypeDetails) { - if (StringUtils.isEmpty(widgetTypeDetails.getName())) { - throw new DataValidationException("Widgets type name should be specified!"); - } - if (StringUtils.isEmpty(widgetTypeDetails.getBundleAlias())) { - throw new DataValidationException("Widgets type bundle alias should be specified!"); - } - if (widgetTypeDetails.getDescriptor() == null || widgetTypeDetails.getDescriptor().size() == 0) { - throw new DataValidationException("Widgets type descriptor can't be empty!"); - } - if (widgetTypeDetails.getTenantId() == null) { - widgetTypeDetails.setTenantId(TenantId.fromUUID(ModelConstants.NULL_UUID)); - } - if (!widgetTypeDetails.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { - Tenant tenant = tenantDao.findById(tenantId, widgetTypeDetails.getTenantId().getId()); - if (tenant == null) { - throw new DataValidationException("Widget type is referencing to non-existent tenant!"); - } - } - } - - @Override - protected void validateCreate(TenantId tenantId, WidgetTypeDetails widgetTypeDetails) { - WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(widgetTypeDetails.getTenantId().getId(), widgetTypeDetails.getBundleAlias()); - if (widgetsBundle == null) { - throw new DataValidationException("Widget type is referencing to non-existent widgets bundle!"); - } - String alias = widgetTypeDetails.getAlias(); - if (alias == null || alias.trim().isEmpty()) { - alias = widgetTypeDetails.getName().toLowerCase().replaceAll("\\W+", "_"); - } - String originalAlias = alias; - int c = 1; - WidgetType withSameAlias; - do { - withSameAlias = widgetTypeDao.findByTenantIdBundleAliasAndAlias(widgetTypeDetails.getTenantId().getId(), widgetTypeDetails.getBundleAlias(), alias); - if (withSameAlias != null) { - alias = originalAlias + (++c); - } - } while (withSameAlias != null); - widgetTypeDetails.setAlias(alias); - } - - @Override - protected void validateUpdate(TenantId tenantId, WidgetTypeDetails widgetTypeDetails) { - WidgetType storedWidgetType = widgetTypeDao.findById(tenantId, widgetTypeDetails.getId().getId()); - if (!storedWidgetType.getTenantId().getId().equals(widgetTypeDetails.getTenantId().getId())) { - throw new DataValidationException("Can't move existing widget type to different tenant!"); - } - if (!storedWidgetType.getBundleAlias().equals(widgetTypeDetails.getBundleAlias())) { - throw new DataValidationException("Update of widget type bundle alias is prohibited!"); - } - if (!storedWidgetType.getAlias().equals(widgetTypeDetails.getAlias())) { - throw new DataValidationException("Update of widget type alias is prohibited!"); - } - } - }; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java index 96e18abd48..df5e4a29f1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java @@ -16,22 +16,17 @@ package org.thingsboard.server.dao.widget; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.widget.WidgetsBundle; -import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; -import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; -import org.thingsboard.server.dao.tenant.TenantDao; import java.util.ArrayList; import java.util.List; @@ -48,10 +43,10 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { private WidgetsBundleDao widgetsBundleDao; @Autowired - private TenantDao tenantDao; + private WidgetTypeService widgetTypeService; @Autowired - private WidgetTypeService widgetTypeService; + private DataValidator widgetsBundleValidator; @Override public WidgetsBundle findWidgetsBundleById(TenantId tenantId, WidgetsBundleId widgetsBundleId) { @@ -150,56 +145,6 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { tenantWidgetsBundleRemover.removeEntities(tenantId, tenantId); } - private DataValidator widgetsBundleValidator = - new DataValidator() { - - @Override - protected void validateDataImpl(TenantId tenantId, WidgetsBundle widgetsBundle) { - if (StringUtils.isEmpty(widgetsBundle.getTitle())) { - throw new DataValidationException("Widgets bundle title should be specified!"); - } - if (widgetsBundle.getTenantId() == null) { - widgetsBundle.setTenantId(TenantId.fromUUID(ModelConstants.NULL_UUID)); - } - if (!widgetsBundle.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { - Tenant tenant = tenantDao.findById(tenantId, widgetsBundle.getTenantId().getId()); - if (tenant == null) { - throw new DataValidationException("Widgets bundle is referencing to non-existent tenant!"); - } - } - } - - @Override - protected void validateCreate(TenantId tenantId, WidgetsBundle widgetsBundle) { - String alias = widgetsBundle.getAlias(); - if (alias == null || alias.trim().isEmpty()) { - alias = widgetsBundle.getTitle().toLowerCase().replaceAll("\\W+", "_"); - } - String originalAlias = alias; - int c = 1; - WidgetsBundle withSameAlias; - do { - withSameAlias = widgetsBundleDao.findWidgetsBundleByTenantIdAndAlias(widgetsBundle.getTenantId().getId(), alias); - if (withSameAlias != null) { - alias = originalAlias + (++c); - } - } while(withSameAlias != null); - widgetsBundle.setAlias(alias); - } - - @Override - protected void validateUpdate(TenantId tenantId, WidgetsBundle widgetsBundle) { - WidgetsBundle storedWidgetsBundle = widgetsBundleDao.findById(tenantId, widgetsBundle.getId().getId()); - if (!storedWidgetsBundle.getTenantId().getId().equals(widgetsBundle.getTenantId().getId())) { - throw new DataValidationException("Can't move existing widgets bundle to different tenant!"); - } - if (!storedWidgetsBundle.getAlias().equals(widgetsBundle.getAlias())) { - throw new DataValidationException("Update of widgets bundle alias is prohibited!"); - } - } - - }; - private PaginatedRemover tenantWidgetsBundleRemover = new PaginatedRemover() { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java index 85951fad5a..770b215adf 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java @@ -418,7 +418,7 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { edge.setType("default"); edge.setSecret(RandomStringUtils.randomAlphanumeric(20)); edge.setRoutingKey(RandomStringUtils.randomAlphanumeric(20)); - edge = edgeService.saveEdge(edge, true); + edge = edgeService.saveEdge(edge); try { dashboardService.assignDashboardToEdge(tenantId, dashboard.getId(), edge.getId()); } finally { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceCredentialsCacheTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceCredentialsCacheTest.java index 8d683abfb8..689752f0fe 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceCredentialsCacheTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceCredentialsCacheTest.java @@ -49,6 +49,9 @@ public abstract class BaseDeviceCredentialsCacheTest extends AbstractServiceTest @Autowired private DeviceCredentialsService deviceCredentialsService; + @Autowired + private DataValidator credentialsValidator; + private DeviceCredentialsDao deviceCredentialsDao; private DeviceService deviceService; @@ -59,10 +62,15 @@ public abstract class BaseDeviceCredentialsCacheTest extends AbstractServiceTest @Before public void setup() throws Exception { - deviceCredentialsDao = mock(DeviceCredentialsDao.class); deviceService = mock(DeviceService.class); + deviceCredentialsDao = mock(DeviceCredentialsDao.class); + + ReflectionTestUtils.setField(credentialsValidator, "deviceService", deviceService); + ReflectionTestUtils.setField(credentialsValidator, "deviceCredentialsDao", deviceCredentialsDao); + ReflectionTestUtils.setField(unwrapDeviceCredentialsService(), "deviceCredentialsDao", deviceCredentialsDao); - ReflectionTestUtils.setField(unwrapDeviceCredentialsService(), "deviceService", deviceService); + ReflectionTestUtils.setField(unwrapDeviceCredentialsService(), "credentialsValidator", credentialsValidator); + } @After diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java index dcf8957b0e..0bbbf82452 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java @@ -60,7 +60,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { @Test public void testSaveEdge() { Edge edge = constructEdge("My edge", "default"); - Edge savedEdge = edgeService.saveEdge(edge, true); + Edge savedEdge = edgeService.saveEdge(edge); Assert.assertNotNull(savedEdge); Assert.assertNotNull(savedEdge.getId()); @@ -72,7 +72,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { savedEdge.setName("My new edge"); - edgeService.saveEdge(savedEdge, true); + edgeService.saveEdge(savedEdge); Edge foundEdge = edgeService.findEdgeById(tenantId, savedEdge.getId()); Assert.assertEquals(foundEdge.getName(), savedEdge.getName()); @@ -84,7 +84,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { Edge edge = new Edge(); edge.setType("default"); edge.setTenantId(tenantId); - edgeService.saveEdge(edge, true); + edgeService.saveEdge(edge); } @Test(expected = DataValidationException.class) @@ -92,7 +92,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { Edge edge = new Edge(); edge.setName("My edge"); edge.setType("default"); - edgeService.saveEdge(edge, true); + edgeService.saveEdge(edge); } @Test(expected = DataValidationException.class) @@ -101,13 +101,13 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { edge.setName("My edge"); edge.setType("default"); edge.setTenantId(TenantId.fromUUID(Uuids.timeBased())); - edgeService.saveEdge(edge, true); + edgeService.saveEdge(edge); } @Test(expected = DataValidationException.class) public void testAssignEdgeToNonExistentCustomer() { Edge edge = constructEdge("My edge", "default"); - edge = edgeService.saveEdge(edge, true); + edge = edgeService.saveEdge(edge); try { edgeService.assignEdgeToCustomer(tenantId, edge.getId(), new CustomerId(Uuids.timeBased())); } finally { @@ -118,7 +118,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { @Test(expected = DataValidationException.class) public void testAssignEdgeToCustomerFromDifferentTenant() { Edge edge = constructEdge("My edge", "default"); - edge = edgeService.saveEdge(edge, true); + edge = edgeService.saveEdge(edge); Tenant tenant = new Tenant(); tenant.setTitle("Test different tenant"); tenant = tenantService.saveTenant(tenant); @@ -137,7 +137,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { @Test public void testFindEdgeById() { Edge edge = constructEdge("My edge", "default"); - Edge savedEdge = edgeService.saveEdge(edge, true); + Edge savedEdge = edgeService.saveEdge(edge); Edge foundEdge = edgeService.findEdgeById(tenantId, savedEdge.getId()); Assert.assertNotNull(foundEdge); Assert.assertEquals(savedEdge, foundEdge); @@ -150,15 +150,15 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { try { for (int i = 0; i < 3; i++) { Edge edge = constructEdge("My edge B" + i, "typeB"); - edges.add(edgeService.saveEdge(edge, true)); + edges.add(edgeService.saveEdge(edge)); } for (int i = 0; i < 7; i++) { Edge edge = constructEdge("My edge C" + i, "typeC"); - edges.add(edgeService.saveEdge(edge, true)); + edges.add(edgeService.saveEdge(edge)); } for (int i = 0; i < 9; i++) { Edge edge = constructEdge("My edge A" + i, "typeA"); - edges.add(edgeService.saveEdge(edge, true)); + edges.add(edgeService.saveEdge(edge)); } List edgeTypes = edgeService.findEdgeTypesByTenantId(tenantId).get(); Assert.assertNotNull(edgeTypes); @@ -176,7 +176,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { @Test public void testDeleteEdge() { Edge edge = constructEdge("My edge", "default"); - Edge savedEdge = edgeService.saveEdge(edge, true); + Edge savedEdge = edgeService.saveEdge(edge); Edge foundEdge = edgeService.findEdgeById(tenantId, savedEdge.getId()); Assert.assertNotNull(foundEdge); edgeService.deleteEdge(tenantId, savedEdge.getId()); @@ -195,7 +195,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { List edges = new ArrayList<>(); for (int i = 0; i < 178; i++) { Edge edge = constructEdge(tenantId, "Edge " + i, "default"); - edges.add(edgeService.saveEdge(edge, true)); + edges.add(edgeService.saveEdge(edge)); } List loadedEdges = new ArrayList<>(); @@ -233,7 +233,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { String name = title1 + suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); Edge edge = constructEdge(name, "default"); - edgesTitle1.add(edgeService.saveEdge(edge, true)); + edgesTitle1.add(edgeService.saveEdge(edge)); } String title2 = "Edge title 2"; List edgesTitle2 = new ArrayList<>(); @@ -242,7 +242,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { String name = title2 + suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); Edge edge = constructEdge(name, "default"); - edgesTitle2.add(edgeService.saveEdge(edge, true)); + edgesTitle2.add(edgeService.saveEdge(edge)); } List loadedEdgesTitle1 = new ArrayList<>(); @@ -305,7 +305,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { String name = title1 + suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); Edge edge = constructEdge(name, type1); - edgesType1.add(edgeService.saveEdge(edge, true)); + edgesType1.add(edgeService.saveEdge(edge)); } String title2 = "Edge title 2"; String type2 = "typeB"; @@ -315,7 +315,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { String name = title2 + suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); Edge edge = constructEdge(name, type2); - edgesType2.add(edgeService.saveEdge(edge, true)); + edgesType2.add(edgeService.saveEdge(edge)); } List loadedEdgesType1 = new ArrayList<>(); @@ -385,7 +385,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { List edges = new ArrayList<>(); for (int i = 0; i < 278; i++) { Edge edge = constructEdge(tenantId, "Edge" + i, "default"); - edge = edgeService.saveEdge(edge, true); + edge = edgeService.saveEdge(edge); edges.add(edgeService.assignEdgeToCustomer(tenantId, edge.getId(), customerId)); } @@ -431,7 +431,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { String name = title1 + suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); Edge edge = constructEdge(name, "default"); - edge = edgeService.saveEdge(edge, true); + edge = edgeService.saveEdge(edge); edgesTitle1.add(edgeService.assignEdgeToCustomer(tenantId, edge.getId(), customerId)); } String title2 = "Edge title 2"; @@ -441,7 +441,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { String name = title2 + suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); Edge edge = constructEdge(name, "default"); - edge = edgeService.saveEdge(edge, true); + edge = edgeService.saveEdge(edge); edgesTitle2.add(edgeService.assignEdgeToCustomer(tenantId, edge.getId(), customerId)); } @@ -513,7 +513,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { String name = title1 + suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); Edge edge = constructEdge(name, type1); - edge = edgeService.saveEdge(edge, true); + edge = edgeService.saveEdge(edge); edgesType1.add(edgeService.assignEdgeToCustomer(tenantId, edge.getId(), customerId)); } String title2 = "Edge title 2"; @@ -524,7 +524,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { String name = title2 + suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); Edge edge = constructEdge(name, type2); - edge = edgeService.saveEdge(edge, true); + edge = edgeService.saveEdge(edge); edgesType2.add(edgeService.assignEdgeToCustomer(tenantId, edge.getId(), customerId)); } @@ -589,12 +589,12 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { String edgeNameAfterRename = RandomStringUtils.randomAlphanumeric(15); Edge edge = constructEdge(tenantId, edgeNameBeforeRename, "default"); - edgeService.saveEdge(edge, true); + edgeService.saveEdge(edge); Edge savedEdge = edgeService.findEdgeByTenantIdAndName(tenantId, edgeNameBeforeRename); savedEdge.setName(edgeNameAfterRename); - edgeService.saveEdge(savedEdge, true); + edgeService.saveEdge(savedEdge); Edge renamedEdge = edgeService.findEdgeByTenantIdAndName(tenantId, edgeNameBeforeRename); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseEntityServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseEntityServiceTest.java index 820b500b8c..430ac85391 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseEntityServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseEntityServiceTest.java @@ -21,8 +21,8 @@ import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomUtils; -import org.hamcrest.Matchers; import org.apache.commons.lang3.StringUtils; +import org.hamcrest.Matchers; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -91,8 +91,8 @@ import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.junit.Assert.assertEquals; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; @Slf4j public abstract class BaseEntityServiceTest extends AbstractServiceTest { @@ -241,7 +241,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { List edges = new ArrayList<>(); for (int i = 0; i < 97; i++) { Edge edge = createEdge(i, "default"); - edges.add(edgeService.saveEdge(edge, true)); + edges.add(edgeService.saveEdge(edge)); } EdgeTypeFilter filter = new EdgeTypeFilter(); @@ -279,7 +279,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { public void testCountHierarchicalEntitiesByEdgeSearchQuery() throws InterruptedException { for (int i = 0; i < 5; i++) { Edge edge = createEdge(i, "type" + i); - edge = edgeService.saveEdge(edge, true); + edge = edgeService.saveEdge(edge); //TO make sure devices have different created time Thread.sleep(1); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java index 861b383e8e..92a3a6af3a 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java @@ -495,7 +495,7 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest { @Test public void testFindEdgeRuleChainsByTenantIdAndName() { Edge edge = constructEdge(tenantId, "My edge", "default"); - Edge savedEdge = edgeService.saveEdge(edge, true); + Edge savedEdge = edgeService.saveEdge(edge); String name1 = "Edge RuleChain name 1"; List ruleChainsName1 = new ArrayList<>(); From 890abe9f98fed0dbfa3fa1866854a68b4d9df004 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Wed, 23 Feb 2022 14:45:42 +0200 Subject: [PATCH 2/3] Added testDeviceReachedMaximumAllowedOnCloud --- .../edge/DefaultEdgeNotificationService.java | 7 +- .../service/edge/rpc/EdgeGrpcSession.java | 1 + .../rpc/processor/DeviceEdgeProcessor.java | 75 ++++++++++++------- .../thingsboard/server/edge/BaseEdgeTest.java | 37 +++++++++ .../server/edge/imitator/EdgeImitator.java | 4 + 5 files changed, 92 insertions(+), 32 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java index 68fecd47fc..55779f19ef 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java @@ -122,7 +122,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { @Override public void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback) { - log.trace("Pushing notification to edge {}", edgeNotificationMsg); + log.debug("Pushing notification to edge {}", edgeNotificationMsg); try { TenantId tenantId = TenantId.fromUUID(new UUID(edgeNotificationMsg.getTenantIdMSB(), edgeNotificationMsg.getTenantIdLSB())); EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); @@ -153,11 +153,12 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { relationProcessor.processRelationNotification(tenantId, edgeNotificationMsg); break; default: - log.debug("Edge event type [{}] is not designed to be pushed to edge", type); + log.warn("Edge event type [{}] is not designed to be pushed to edge", type); } } catch (Exception e) { callback.onFailure(e); - log.error("Can't push to edge updates, edgeNotificationMsg [{}]", edgeNotificationMsg, e); + String errMsg = String.format("Can't push to edge updates, edgeNotificationMsg [%s]", edgeNotificationMsg); + log.error(errMsg, e); } finally { callback.onSuccess(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java index fdcbdbd849..a35f3efb65 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java @@ -644,6 +644,7 @@ public final class EdgeGrpcSession implements Closeable { } } catch (Exception e) { log.error("[{}] Can't process uplink msg [{}]", this.sessionId, uplinkMsg, e); + return Futures.immediateFailedFuture(e); } return Futures.allAsList(result); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceEdgeProcessor.java index 8a6697d7b1..f19ec25b27 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceEdgeProcessor.java @@ -51,6 +51,7 @@ import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; +import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg; @@ -79,38 +80,37 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor { String deviceName = deviceUpdateMsg.getName(); Device device = deviceService.findDeviceByTenantIdAndName(tenantId, deviceName); if (device != null) { - PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE); - PageData pageData; - do { - pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, device.getId(), pageLink); - boolean update = false; - if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { - if (pageData.getData().contains(edge.getId())) { - update = true; - } - if (pageData.hasNext()) { - pageLink = pageLink.nextPageLink(); - } + boolean deviceAlreadyExistsForThisEdge = isDeviceAlreadyExistsOnCloudForThisEdge(tenantId, edge, device); + if (deviceAlreadyExistsForThisEdge) { + log.info("[{}] Device with name '{}' already exists on the cloud, and related to this edge [{}]. " + + "deviceUpdateMsg [{}], Updating device", tenantId, deviceName, edge.getId(), deviceUpdateMsg); + updateDevice(tenantId, edge, deviceUpdateMsg); + } else { + log.info("[{}] Device with name '{}' already exists on the cloud, but not related to this edge [{}]. deviceUpdateMsg [{}]." + + "Creating a new device with random prefix and relate to this edge", tenantId, deviceName, edge.getId(), deviceUpdateMsg); + String newDeviceName = deviceUpdateMsg.getName() + "_" + RandomStringUtils.randomAlphabetic(15); + Device newDevice; + try { + newDevice = createDevice(tenantId, edge, deviceUpdateMsg, newDeviceName); + } catch (DataValidationException e) { + String errMsg = String.format("[%s] Device update msg can't be processed due to data validation [%s]", tenantId, deviceUpdateMsg); + log.error(errMsg, e); + return Futures.immediateFuture(null); } - - if (update) { - log.info("[{}] Device with name '{}' already exists on the cloud, and related to this edge [{}]. " + - "deviceUpdateMsg [{}], Updating device", tenantId, deviceName, edge.getId(), deviceUpdateMsg); - updateDevice(tenantId, edge, deviceUpdateMsg); - } else { - log.info("[{}] Device with name '{}' already exists on the cloud, but not related to this edge [{}]. deviceUpdateMsg [{}]." + - "Creating a new device with random prefix and relate to this edge", tenantId, deviceName, edge.getId(), deviceUpdateMsg); - String newDeviceName = deviceUpdateMsg.getName() + "_" + RandomStringUtils.randomAlphabetic(15); - Device newDevice = createDevice(tenantId, edge, deviceUpdateMsg, newDeviceName); - ObjectNode body = mapper.createObjectNode(); - body.put("conflictName", deviceName); - saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, newDevice.getId(), body); - saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, newDevice.getId(), null); - } - } while (pageData != null && pageData.hasNext()); + ObjectNode body = mapper.createObjectNode(); + body.put("conflictName", deviceName); + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, newDevice.getId(), body); + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, newDevice.getId(), null); + } } else { log.info("[{}] Creating new device and replacing device entity on the edge [{}]", tenantId, deviceUpdateMsg); - device = createDevice(tenantId, edge, deviceUpdateMsg, deviceUpdateMsg.getName()); + try { + device = createDevice(tenantId, edge, deviceUpdateMsg, deviceUpdateMsg.getName()); + } catch (DataValidationException e) { + String errMsg = String.format("[%s] Device update msg can't be processed due to data validation [%s]", tenantId, deviceUpdateMsg); + log.error(errMsg, e); + return Futures.immediateFuture(null); + } saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, device.getId(), null); } break; @@ -131,6 +131,23 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor { return Futures.immediateFuture(null); } + private boolean isDeviceAlreadyExistsOnCloudForThisEdge(TenantId tenantId, Edge edge, Device device) { + PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE); + PageData pageData; + do { + pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, device.getId(), pageLink); + if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { + if (pageData.getData().contains(edge.getId())) { + return true; + } + if (pageData.hasNext()) { + pageLink = pageLink.nextPageLink(); + } + } + } while (pageData != null && pageData.hasNext()); + return false; + } + public ListenableFuture processDeviceCredentialsFromEdge(TenantId tenantId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) { log.debug("Executing onDeviceCredentialsUpdate, deviceCredentialsUpdateMsg [{}]", deviceCredentialsUpdateMsg); DeviceId deviceId = new DeviceId(new UUID(deviceCredentialsUpdateMsg.getDeviceIdMSB(), deviceCredentialsUpdateMsg.getDeviceIdLSB())); diff --git a/application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java index 9392448c6c..bb396bf56e 100644 --- a/application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java @@ -42,6 +42,7 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmInfo; @@ -80,6 +81,7 @@ import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.common.transport.adaptor.JsonConverter; @@ -110,6 +112,7 @@ import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UplinkMsg; +import org.thingsboard.server.gen.edge.v1.UplinkResponseMsg; import org.thingsboard.server.gen.edge.v1.UserCredentialsRequestMsg; import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg; import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; @@ -385,6 +388,40 @@ abstract public class BaseEdgeTest extends AbstractControllerTest { } + @Test + public void testDeviceReachedMaximumAllowedOnCloud() throws Exception { + // update tenant profile configuration + loginSysAdmin(); + TenantProfile tenantProfile = doGet("/api/tenantProfile/" + savedTenant.getTenantProfileId().getId(), TenantProfile.class); + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration(); + profileConfiguration.setMaxDevices(1); + tenantProfile.getProfileData().setConfiguration(profileConfiguration); + doPost("/api/tenantProfile/", tenantProfile, TenantProfile.class); + + loginTenantAdmin(); + + UUID uuid = Uuids.timeBased(); + + UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); + DeviceUpdateMsg.Builder deviceUpdateMsgBuilder = DeviceUpdateMsg.newBuilder(); + deviceUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); + deviceUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); + deviceUpdateMsgBuilder.setName("Edge Device"); + deviceUpdateMsgBuilder.setType("default"); + deviceUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); + uplinkMsgBuilder.addDeviceUpdateMsg(deviceUpdateMsgBuilder.build()); + + edgeImitator.expectResponsesAmount(1); + + edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); + + Assert.assertTrue(edgeImitator.waitForResponses()); + + UplinkResponseMsg latestResponseMsg = edgeImitator.getLatestResponseMsg(); + Assert.assertTrue(latestResponseMsg.getSuccess()); + } + @Test public void testAssets() throws Exception { // 1 diff --git a/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java b/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java index 89c61f5a14..cfb652dd9f 100644 --- a/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java +++ b/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java @@ -88,6 +88,9 @@ public class EdgeImitator { @Getter private List downlinkMsgs; + @Getter + private UplinkResponseMsg latestResponseMsg; + private boolean connected = false; public EdgeImitator(String host, int port, String routingKey, String routingSecret) throws NoSuchFieldException, IllegalAccessException { @@ -132,6 +135,7 @@ public class EdgeImitator { private void onUplinkResponse(UplinkResponseMsg msg) { log.info("onUplinkResponse: {}", msg); + latestResponseMsg = msg; responsesLatch.countDown(); } From b75f949004d7226b3b6c8d0acb11e40d363783f9 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Wed, 23 Feb 2022 14:48:32 +0200 Subject: [PATCH 3/3] Fixed error msg logging --- .../thingsboard/server/service/edge/rpc/EdgeGrpcSession.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java index a35f3efb65..e4f32f6a89 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java @@ -643,7 +643,8 @@ public final class EdgeGrpcSession implements Closeable { } } } catch (Exception e) { - log.error("[{}] Can't process uplink msg [{}]", this.sessionId, uplinkMsg, e); + String errMsg = String.format("[%s] Can't process uplink msg [%s]", this.sessionId, uplinkMsg); + log.error(errMsg, e); return Futures.immediateFailedFuture(e); } return Futures.allAsList(result);