Browse Source

Merge pull request #6139 from volodymyr-babak/validators-refactoring

[3.3.4][Edge] Refactoring validators - moved them to a separate classes. Added device profile validation in edge processor
pull/6174/head
Andrew Shvayka 4 years ago
committed by GitHub
parent
commit
fd6bf9b0fd
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      application/src/main/java/org/thingsboard/server/controller/EdgeController.java
  2. 11
      application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java
  3. 2
      application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java
  4. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
  5. 7
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java
  6. 86
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceEdgeProcessor.java
  7. 37
      application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java
  8. 4
      application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java
  9. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java
  10. 37
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
  11. 85
      dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
  12. 21
      dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java
  13. 30
      dao/src/main/java/org/thingsboard/server/dao/cache/EntitiesCacheManager.java
  14. 64
      dao/src/main/java/org/thingsboard/server/dao/cache/EntitiesCacheManagerImpl.java
  15. 24
      dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java
  16. 66
      dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java
  17. 39
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java
  18. 55
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java
  19. 490
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java
  20. 147
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  21. 17
      dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java
  22. 93
      dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java
  23. 67
      dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java
  24. 19
      dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java
  25. 44
      dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java
  26. 180
      dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java
  27. 60
      dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java
  28. 69
      dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java
  29. 61
      dao/src/main/java/org/thingsboard/server/dao/service/validator/AdminSettingsDataValidator.java
  30. 57
      dao/src/main/java/org/thingsboard/server/dao/service/validator/AlarmDataValidator.java
  31. 50
      dao/src/main/java/org/thingsboard/server/dao/service/validator/ApiUsageDataValidator.java
  32. 108
      dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java
  33. 39
      dao/src/main/java/org/thingsboard/server/dao/service/validator/AuditLogDataValidator.java
  34. 123
      dao/src/main/java/org/thingsboard/server/dao/service/validator/BaseOtaPackageDataValidator.java
  35. 51
      dao/src/main/java/org/thingsboard/server/dao/service/validator/ClientRegistrationTemplateDataValidator.java
  36. 43
      dao/src/main/java/org/thingsboard/server/dao/service/validator/ComponentDescriptorDataValidator.java
  37. 92
      dao/src/main/java/org/thingsboard/server/dao/service/validator/CustomerDataValidator.java
  38. 68
      dao/src/main/java/org/thingsboard/server/dao/service/validator/DashboardDataValidator.java
  39. 75
      dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceCredentialsDataValidator.java
  40. 147
      dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceDataValidator.java
  41. 524
      dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java
  42. 90
      dao/src/main/java/org/thingsboard/server/dao/service/validator/EdgeDataValidator.java
  43. 36
      dao/src/main/java/org/thingsboard/server/dao/service/validator/EdgeEventDataValidator.java
  44. 88
      dao/src/main/java/org/thingsboard/server/dao/service/validator/EntityViewDataValidator.java
  45. 40
      dao/src/main/java/org/thingsboard/server/dao/service/validator/EventDataValidator.java
  46. 105
      dao/src/main/java/org/thingsboard/server/dao/service/validator/OtaPackageDataValidator.java
  47. 40
      dao/src/main/java/org/thingsboard/server/dao/service/validator/OtaPackageInfoDataValidator.java
  48. 82
      dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java
  49. 88
      dao/src/main/java/org/thingsboard/server/dao/service/validator/RuleChainDataValidator.java
  50. 68
      dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantDataValidator.java
  51. 69
      dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java
  52. 68
      dao/src/main/java/org/thingsboard/server/dao/service/validator/UserCredentialsDataValidator.java
  53. 136
      dao/src/main/java/org/thingsboard/server/dao/service/validator/UserDataValidator.java
  54. 98
      dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetTypeDataValidator.java
  55. 81
      dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetsBundleDataValidator.java
  56. 38
      dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java
  57. 50
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java
  58. 39
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java
  59. 31
      dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java
  60. 141
      dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java
  61. 71
      dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java
  62. 59
      dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java
  63. 2
      dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java
  64. 12
      dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceCredentialsCacheTest.java
  65. 48
      dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java
  66. 8
      dao/src/test/java/org/thingsboard/server/dao/service/BaseEntityServiceTest.java
  67. 2
      dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java

2
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;

11
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;
}
@ -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();
}

2
application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java

@ -68,7 +68,7 @@ public class EdgeBulkImportService extends AbstractBulkImportService<Edge> {
@Override
protected Edge saveEntity(Edge entity, Map<BulkImportColumnType, String> fields) {
return edgeService.saveEdge(entity, true);
return edgeService.saveEdge(entity);
}
@Override

4
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java

@ -643,7 +643,9 @@ 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);
}

7
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<Device> deviceValidator;
@Autowired
protected EntityDataMsgConstructor entityDataMsgConstructor;

86
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,14 @@ 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.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;
@ -59,7 +61,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;
@ -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<EdgeId> 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<EdgeId> 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<Void> processDeviceCredentialsFromEdge(TenantId tenantId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) {
log.debug("Executing onDeviceCredentialsUpdate, deviceCredentialsUpdateMsg [{}]", deviceCredentialsUpdateMsg);
DeviceId deviceId = new DeviceId(new UUID(deviceCredentialsUpdateMsg.getDeviceIdMSB(), deviceCredentialsUpdateMsg.getDeviceIdLSB()));
@ -194,7 +211,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 +230,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) {

37
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;
@ -413,6 +416,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

4
application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java

@ -88,6 +88,9 @@ public class EdgeImitator {
@Getter
private List<AbstractMessage> 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();
}

2
common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java

@ -42,7 +42,7 @@ public interface EdgeService {
Optional<Edge> findEdgeByRoutingKey(TenantId tenantId, String routingKey);
Edge saveEdge(Edge edge, boolean doValidate);
Edge saveEdge(Edge edge);
Edge assignEdgeToCustomer(TenantId tenantId, EdgeId edgeId, CustomerId customerId);

37
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<Alarm> alarmDataValidator;
protected ExecutorService readResultsProcessingExecutor;
@ -412,32 +407,4 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
ListenableFuture<Alarm> entity = alarmDao.findAlarmByIdAsync(tenantId, alarmId.getId());
return Futures.transform(entity, function, readResultsProcessingExecutor);
}
private DataValidator<Alarm> 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!");
}
}
}
};
}

85
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<Asset> 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<Asset> 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<Asset> assetValidator =
new DataValidator<Asset>() {
@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<TenantId, Asset> tenantAssetsRemover =
new PaginatedRemover<TenantId, Asset>() {

21
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<AuditLog> auditLogValidator;
@Override
public PageData<AuditLog> findAuditLogsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, List<ActionType> 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<AuditLog> auditLogValidator =
new DataValidator<AuditLog>() {
@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!");
}
}
};
}

30
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);
}

64
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));
}
}

24
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<ComponentDescriptor> 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<ComponentDescriptor> componentValidator =
new DataValidator<ComponentDescriptor>() {
@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!");
}
}
};
}

66
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<Customer> 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<Customer> customerValidator =
new DataValidator<Customer>() {
@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<TenantId, Customer> customersByTenantRemover =
new PaginatedRemover<TenantId, Customer>() {

39
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<Dashboard> 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<Dashboard> dashboardValidator =
new DataValidator<Dashboard>() {
@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<TenantId, DashboardInfo> tenantDashboardsRemover =
new PaginatedRemover<TenantId, DashboardInfo>() {

55
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<DeviceCredentials> 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<DeviceCredentials> credentialsValidator =
new DataValidator<DeviceCredentials>() {
@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!");
}
}
};
}

490
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<DeviceProfile> deviceProfileValidator;
private final Lock findOrCreateLock = new ReentrantLock();
@ -359,426 +291,6 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
tenantDeviceProfilesRemover.removeEntities(tenantId, tenantId);
}
private DataValidator<DeviceProfile> 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<LwM2MBootstrapServerCredential> lwM2MBootstrapServersConfigurations = ((Lwm2mDeviceProfileTransportConfiguration) transportConfiguration).getBootstrap();
if (lwM2MBootstrapServersConfigurations != null) {
validateLwm2mServersConfigOfBootstrapForClient(lwM2MBootstrapServersConfigurations,
((Lwm2mDeviceProfileTransportConfiguration) transportConfiguration).isBootstrapServerUpdateEnable());
for (LwM2MBootstrapServerCredential bootstrapServerCredential : lwM2MBootstrapServersConfigurations) {
validateLwm2mServersCredentialOfBootstrapForClient(bootstrapServerCredential);
}
}
}
List<DeviceProfileAlarm> profileAlarms = deviceProfile.getProfileData().getAlarms();
if (!CollectionUtils.isEmpty(profileAlarms)) {
Set<String> 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<TypeElement> 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<FieldElement> 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<EnumElement> 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<MessageElement> 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<OneOfElement> 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<TypeElement> nestedTypes = messageElement.getNestedTypes();
if (!nestedTypes.isEmpty()) {
List<EnumElement> nestedEnumTypes = getEnumElements(nestedTypes);
if (!nestedEnumTypes.isEmpty()) {
checkEnumElements(schemaName, nestedEnumTypes);
}
List<MessageElement> nestedMessageTypes = getMessageTypes(nestedTypes);
checkMessageElements(schemaName, nestedMessageTypes);
}
});
}
}
private List<MessageElement> getMessageTypes(List<TypeElement> types) {
return types.stream()
.filter(typeElement -> typeElement instanceof MessageElement)
.map(typeElement -> (MessageElement) typeElement)
.collect(Collectors.toList());
}
private List<EnumElement> getEnumElements(List<TypeElement> 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<Descriptors.FieldDescriptor> 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<LwM2MBootstrapServerCredential> lwM2MBootstrapServersConfigurations, boolean isBootstrapServerUpdateEnable) {
Set<String> uris = new HashSet<>();
Set<Integer> 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<TenantId, DeviceProfile> tenantDeviceProfilesRemover =
new PaginatedRemover<TenantId, DeviceProfile>() {

147
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<Device> 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<Device> 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<Device> deviceValidator =
new DataValidator<Device>() {
@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<TenantId, Device> tenantDevicesRemover =
new PaginatedRemover<TenantId, Device>() {

17
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<EdgeEvent> 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<EdgeEvent> edgeEventValidator =
new DataValidator<EdgeEvent>() {
@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!");
}
}
};
}

93
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<Edge> 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<Edge> 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<Edge> apply(@Nullable List<Edge> 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<Edge> edgeValidator =
new DataValidator<Edge>() {
@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<TenantId, Edge> tenantEdgesRemover =
new PaginatedRemover<TenantId, Edge>() {

67
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<EntityView> 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<EntityView> entityViewValidator =
new DataValidator<EntityView>() {
@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<TenantId, EntityView> tenantEntityViewRemover = new PaginatedRemover<TenantId, EntityView>() {
@Override
protected PageData<EntityView> findEntities(TenantId tenantId, TenantId id, PageLink pageLink) {

19
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<Event> eventValidator;
@Override
public ListenableFuture<Void> 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<Event> eventValidator =
new DataValidator<Event>() {
@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!.");
}
}
};
}

44
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<OAuth2ClientRegistrationTemplate> 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<OAuth2ClientRegistrationTemplate> clientRegistrationTemplateValidator =
new DataValidator<OAuth2ClientRegistrationTemplate>() {
@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!");
}
}
};
}

180
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<OtaPackageInfo> otaPackageInfoValidator;
private final DataValidator<OtaPackage> otaPackageValidator;
@Override
public OtaPackageInfo saveOtaPackageInfo(OtaPackageInfo otaPackageInfo, boolean isUrl) {
@ -230,168 +216,6 @@ public class BaseOtaPackageService implements OtaPackageService {
tenantOtaPackageRemover.removeEntities(tenantId, tenantId);
}
private DataValidator<OtaPackageInfo> 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<OtaPackage> 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<TenantId, OtaPackageInfo> tenantOtaPackageRemover =
new PaginatedRemover<>() {

60
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<TbResource> resourceValidator;
@Override
public TbResource saveResource(TbResource resource) {
@ -153,45 +139,7 @@ public class BaseResourceService implements ResourceService {
return resourceDao.sumDataSizeByTenantId(tenantId);
}
private DataValidator<TbResource> 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<TenantId, TbResource> tenantResourcesRemover =
private final PaginatedRemover<TenantId, TbResource> tenantResourcesRemover =
new PaginatedRemover<>() {
@Override

69
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<RuleChain> ruleChainValidator;
@Override
@Transactional
@ -519,23 +509,6 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
}
}
private List<RuleChain> findAllTenantRuleChains(TenantId tenantId, RuleChainType type) {
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
return findAllTenantRuleChainsRecursive(tenantId, new ArrayList<>(), type, pageLink);
}
private List<RuleChain> findAllTenantRuleChainsRecursive(TenantId tenantId, List<RuleChain> accumulator, RuleChainType type, PageLink pageLink) {
PageData<RuleChain> persistentRuleChainData = findTenantRuleChainsByType(tenantId, type, pageLink);
List<RuleChain> 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<RuleChainMetaData> 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<RuleChain> 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<TenantId, RuleChain> tenantRuleChainsRemover =
new PaginatedRemover<>() {

61
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<AdminSettings> {
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!");
}
}
}

57
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<Alarm> {
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!");
}
}
}
}

50
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<ApiUsageState> {
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!");
}
}
}

108
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<Asset> {
@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!");
}
}
}
}

39
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<AuditLog> {
@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!");
}
}
}

123
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<D extends BaseData<?>> extends DataValidator<D> {
@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!");
}
}
}

51
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<OAuth2ClientRegistrationTemplate> {
@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!");
}
}
}

43
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<ComponentDescriptor> {
@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!");
}
}
}

92
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<Customer> {
@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!");
}
}
}
}

68
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<Dashboard> {
@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!");
}
}
}
}

75
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<DeviceCredentials> {
@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!");
}
}
}

147
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<Device> {
@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!");
}
}
}
}

524
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<DeviceProfile> {
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<LwM2MBootstrapServerCredential> lwM2MBootstrapServersConfigurations = ((Lwm2mDeviceProfileTransportConfiguration) transportConfiguration).getBootstrap();
if (lwM2MBootstrapServersConfigurations != null) {
validateLwm2mServersConfigOfBootstrapForClient(lwM2MBootstrapServersConfigurations,
((Lwm2mDeviceProfileTransportConfiguration) transportConfiguration).isBootstrapServerUpdateEnable());
for (LwM2MBootstrapServerCredential bootstrapServerCredential : lwM2MBootstrapServersConfigurations) {
validateLwm2mServersCredentialOfBootstrapForClient(bootstrapServerCredential);
}
}
}
List<DeviceProfileAlarm> profileAlarms = deviceProfile.getProfileData().getAlarms();
if (!CollectionUtils.isEmpty(profileAlarms)) {
Set<String> 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<TypeElement> 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<FieldElement> 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<EnumElement> 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<MessageElement> 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<OneOfElement> 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<TypeElement> nestedTypes = messageElement.getNestedTypes();
if (!nestedTypes.isEmpty()) {
List<EnumElement> nestedEnumTypes = getEnumElements(nestedTypes);
if (!nestedEnumTypes.isEmpty()) {
checkEnumElements(schemaName, nestedEnumTypes);
}
List<MessageElement> nestedMessageTypes = getMessageTypes(nestedTypes);
checkMessageElements(schemaName, nestedMessageTypes);
}
});
}
}
private List<MessageElement> getMessageTypes(List<TypeElement> types) {
return types.stream()
.filter(typeElement -> typeElement instanceof MessageElement)
.map(typeElement -> (MessageElement) typeElement)
.collect(Collectors.toList());
}
private List<EnumElement> getEnumElements(List<TypeElement> 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<Descriptors.FieldDescriptor> 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<LwM2MBootstrapServerCredential> lwM2MBootstrapServersConfigurations, boolean isBootstrapServerUpdateEnable) {
Set<String> uris = new HashSet<>();
Set<Integer> 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;
}
}
}

90
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<Edge> {
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!");
}
}
}
}

36
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<EdgeEvent> {
@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!");
}
}
}

88
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<EntityView> {
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!");
}
}
}
}

40
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<Event> {
@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!.");
}
}
}

105
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<OtaPackage> {
@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);
}
}
}

40
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<OtaPackageInfo> {
@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);
}
}

82
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<TbResource> {
@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!");
}
}
}
}

88
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<RuleChain> {
@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!");
}
}
}
}

68
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<Tenant> {
@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!");
}
}
}

69
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<TenantProfile> {
@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!");
}
}
}

68
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<UserCredentials> {
@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!");
}
}
}

136
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<User> {
@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!");
}
}
}
}

98
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<WidgetTypeDetails> {
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!");
}
}
}

81
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<WidgetsBundle> {
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!");
}
}
}

38
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<AdminSettings> 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<AdminSettings> adminSettingsValidator =
new DataValidator<AdminSettings>() {
@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!");
}
}
};
}

50
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<TenantProfile> 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<TenantProfile> tenantProfileValidator =
new DataValidator<TenantProfile>() {
@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<String, TenantProfile> tenantProfilesRemover =
private final PaginatedRemover<String, TenantProfile> tenantProfilesRemover =
new PaginatedRemover<String, TenantProfile>() {
@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());
}
};

39
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<Tenant> 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<Tenant> tenantValidator =
new DataValidator<Tenant>() {
@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<TenantId, Tenant> tenantsRemover =
new PaginatedRemover<>() {

31
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<ApiUsageState> 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<ApiUsageState> apiUsageStateValidator =
new DataValidator<ApiUsageState>() {
@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!");
}
}
};
}

141
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<User> userValidator;
private final DataValidator<UserCredentials> userCredentialsValidator;
private final ApplicationEventPublisher eventPublisher;
public UserServiceImpl(UserDao userDao,
UserCredentialsDao userCredentialsDao,
TenantDao tenantDao,
CustomerDao customerDao,
@Lazy TbTenantProfileCache tenantProfileCache,
DataValidator<User> userValidator,
DataValidator<UserCredentials> 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<User> 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<UserCredentials> 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<TenantId, User> tenantAdminsRemover = new PaginatedRemover<>() {
@Override
protected PageData<User> findEntities(TenantId tenantId, TenantId id, PageLink pageLink) {

71
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<WidgetTypeDetails> widgetTypeValidator;
@Override
public WidgetType findWidgetTypeById(TenantId tenantId, WidgetTypeId widgetTypeId) {
@ -121,64 +112,4 @@ public class WidgetTypeServiceImpl implements WidgetTypeService {
}
}
private DataValidator<WidgetTypeDetails> 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!");
}
}
};
}

59
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<WidgetsBundle> widgetsBundleValidator;
@Override
public WidgetsBundle findWidgetsBundleById(TenantId tenantId, WidgetsBundleId widgetsBundleId) {
@ -150,56 +145,6 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService {
tenantWidgetsBundleRemover.removeEntities(tenantId, tenantId);
}
private DataValidator<WidgetsBundle> widgetsBundleValidator =
new DataValidator<WidgetsBundle>() {
@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<TenantId, WidgetsBundle> tenantWidgetsBundleRemover =
new PaginatedRemover<TenantId, WidgetsBundle>() {

2
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 {

12
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<DeviceCredentials> 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

48
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<EntitySubtype> 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<Edge> 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<Edge> 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<Edge> 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<Edge> 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<Edge> loadedEdgesType1 = new ArrayList<>();
@ -385,7 +385,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest {
List<Edge> 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);

8
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<Edge> 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);

2
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<RuleChain> ruleChainsName1 = new ArrayList<>();

Loading…
Cancel
Save