Browse Source

Merge branch 'master' of github.com:thingsboard/thingsboard

pull/4537/head
Igor Kulikov 5 years ago
parent
commit
cf405be4f3
  1. 20
      application/src/main/data/upgrade/3.2.2/schema_update.sql
  2. 4
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  3. 9
      application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
  4. 15
      application/src/main/java/org/thingsboard/server/controller/FirmwareController.java
  5. 4
      application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java
  6. 24
      application/src/main/java/org/thingsboard/server/controller/UserController.java
  7. 141
      application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java
  8. 2
      application/src/main/java/org/thingsboard/server/service/firmware/FirmwareStateService.java
  9. 113
      application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java
  10. 2
      application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java
  11. 10
      application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java
  12. 61
      application/src/main/resources/thingsboard.yml
  13. 30
      application/src/test/java/org/thingsboard/server/controller/BaseFirmwareControllerTest.java
  14. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
  15. 4
      common/dao-api/src/main/java/org/thingsboard/server/dao/firmware/FirmwareService.java
  16. 36
      common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java
  17. 14
      common/data/src/main/java/org/thingsboard/server/common/data/Device.java
  18. 6
      common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java
  19. 6
      common/data/src/main/java/org/thingsboard/server/common/data/FirmwareInfo.java
  20. 25
      common/data/src/main/java/org/thingsboard/server/common/data/HasFirmware.java
  21. 13
      common/data/src/main/java/org/thingsboard/server/common/data/device/profile/MqttTopics.java
  22. 30
      common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareKey.java
  23. 30
      common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareType.java
  24. 91
      common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUtil.java
  25. 2
      common/message/src/main/java/org/thingsboard/server/common/msg/session/FeatureType.java
  26. 3
      common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java
  27. 11
      common/queue/src/main/proto/queue.proto
  28. 23
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
  29. 37
      common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java
  30. 61
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java
  31. 12
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportContextBootstrap.java
  32. 33
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java
  33. 27
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/client/LwM2mSoftwareUpdate.java
  34. 38
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MSecureServerConfig.java
  35. 47
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportBootstrapConfig.java
  36. 162
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java
  37. 29
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java
  38. 395
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java
  39. 160
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java
  40. 22
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java
  41. 99
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java
  42. 20
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java
  43. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java
  44. 32
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContext.java
  45. 37
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportHandlerUtil.java
  46. 6
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportMsgHandler.java
  47. 205
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java
  48. 85
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java
  49. 31
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerInitializer.java
  50. 16
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java
  51. 21
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MJsonAdaptor.java
  52. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MTransportAdaptor.java
  53. 26
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java
  54. 18
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java
  55. 27
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFirmwareUpdate.java
  56. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java
  57. 0
      common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh
  58. 0
      common/transport/lwm2m/src/main/resources/lwm2mserver.jks
  59. 88
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
  60. 8
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java
  61. 3
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java
  62. 7
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/ProtoMqttAdaptor.java
  63. 4
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java
  64. 217
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java
  65. 2
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
  66. 23
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java
  67. 34
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  68. 96
      dao/src/main/java/org/thingsboard/server/dao/firmware/BaseFirmwareService.java
  69. 7
      dao/src/main/java/org/thingsboard/server/dao/firmware/FirmwareInfoDao.java
  70. 4
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  71. 16
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java
  72. 18
      dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java
  73. 21
      dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareEntity.java
  74. 27
      dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareInfoEntity.java
  75. 9
      dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java
  76. 10
      dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java
  77. 28
      dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareInfoRepository.java
  78. 14
      dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareInfoDao.java
  79. 10
      dao/src/main/resources/sql/schema-entities-hsql.sql
  80. 1
      dao/src/main/resources/sql/schema-entities-idx.sql
  81. 20
      dao/src/main/resources/sql/schema-entities.sql
  82. 5
      dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java
  83. 43
      dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java
  84. 232
      dao/src/test/java/org/thingsboard/server/dao/service/BaseFirmwareServiceTest.java
  85. 0
      transport/lwm2m/src/main/data/lwm2mserver.jks
  86. 2
      ui-ngx/src/app/core/http/entity.service.ts
  87. 15
      ui-ngx/src/app/core/http/firmware.service.ts
  88. 94
      ui-ngx/src/app/core/services/menu.service.ts
  89. 4
      ui-ngx/src/app/modules/home/components/device/device-credentials.component.html
  90. 75
      ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts
  91. 79
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.html
  92. 20
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.scss
  93. 176
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.ts
  94. 143
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.html
  95. 34
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.scss
  96. 257
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.ts
  97. 3
      ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.html
  98. 6
      ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.ts
  99. 3
      ui-ngx/src/app/modules/home/components/event/event-table-config.ts
  100. 6
      ui-ngx/src/app/modules/home/components/home-components.module.ts

20
application/src/main/data/upgrade/3.2.2/schema_update.sql

@ -63,6 +63,8 @@ CREATE TABLE IF NOT EXISTS firmware (
id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY,
created_time bigint NOT NULL,
tenant_id uuid NOT NULL,
device_profile_id uuid,
type varchar(32) NOT NULL,
title varchar(255) NOT NULL,
version varchar(255) NOT NULL,
file_name varchar(255),
@ -77,10 +79,12 @@ CREATE TABLE IF NOT EXISTS firmware (
);
ALTER TABLE device_profile
ADD COLUMN IF NOT EXISTS firmware_id uuid;
ADD COLUMN IF NOT EXISTS firmware_id uuid,
ADD COLUMN IF NOT EXISTS software_id uuid;
ALTER TABLE device
ADD COLUMN IF NOT EXISTS firmware_id uuid;
ADD COLUMN IF NOT EXISTS firmware_id uuid,
ADD COLUMN IF NOT EXISTS software_id uuid;
DO $$
BEGIN
@ -90,11 +94,23 @@ DO $$
FOREIGN KEY (firmware_id) REFERENCES firmware(id);
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device_profile') THEN
ALTER TABLE device_profile
ADD CONSTRAINT fk_software_device_profile
FOREIGN KEY (firmware_id) REFERENCES firmware(id);
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device') THEN
ALTER TABLE device
ADD CONSTRAINT fk_firmware_device
FOREIGN KEY (firmware_id) REFERENCES firmware(id);
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device') THEN
ALTER TABLE device
ADD CONSTRAINT fk_software_device
FOREIGN KEY (firmware_id) REFERENCES firmware(id);
END IF;
END;
$$;

4
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -132,7 +132,7 @@ import org.thingsboard.server.service.firmware.FirmwareStateService;
import org.thingsboard.server.service.edge.EdgeNotificationService;
import org.thingsboard.server.service.edge.rpc.EdgeGrpcService;
import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService;
import org.thingsboard.server.service.lwm2m.LwM2MModelsRepository;
import org.thingsboard.server.service.lwm2m.LwM2MServerSecurityInfoRepository;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.service.resource.TbResourceService;
@ -267,7 +267,7 @@ public abstract class BaseController {
protected TbDeviceProfileCache deviceProfileCache;
@Autowired
protected LwM2MModelsRepository lwM2MModelsRepository;
protected LwM2MServerSecurityInfoRepository lwM2MServerSecurityInfoRepository;
@Autowired(required = false)
protected EdgeService edgeService;

9
application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java

@ -146,12 +146,16 @@ public class DeviceProfileController extends BaseController {
checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE);
boolean isFirmwareChanged = false;
boolean isSoftwareChanged = false;
if (!created) {
DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), deviceProfile.getId());
if (!Objects.equals(deviceProfile.getFirmwareId(), oldDeviceProfile.getFirmwareId())) {
isFirmwareChanged = true;
}
if (!Objects.equals(deviceProfile.getSoftwareId(), oldDeviceProfile.getSoftwareId())) {
isSoftwareChanged = true;
}
}
DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile));
@ -164,9 +168,8 @@ public class DeviceProfileController extends BaseController {
null,
created ? ActionType.ADDED : ActionType.UPDATED, null);
if (isFirmwareChanged) {
firmwareStateService.update(savedDeviceProfile);
}
firmwareStateService.update(savedDeviceProfile, isFirmwareChanged, isSoftwareChanged);
sendEntityNotificationMsg(getTenantId(), savedDeviceProfile.getId(),
deviceProfile.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
return savedDeviceProfile;

15
application/src/main/java/org/thingsboard/server/controller/FirmwareController.java

@ -35,6 +35,8 @@ import org.thingsboard.server.common.data.Firmware;
import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
@ -133,6 +135,8 @@ public class FirmwareController extends BaseController {
Firmware firmware = new Firmware(firmwareId);
firmware.setCreatedTime(info.getCreatedTime());
firmware.setTenantId(getTenantId());
firmware.setDeviceProfileId(info.getDeviceProfileId());
firmware.setType(info.getType());
firmware.setTitle(info.getTitle());
firmware.setVersion(info.getVersion());
firmware.setAdditionalInfo(info.getAdditionalInfo());
@ -175,17 +179,22 @@ public class FirmwareController extends BaseController {
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/firmwares/{hasData}", method = RequestMethod.GET)
@RequestMapping(value = "/firmwares/{deviceProfileId}/{type}/{hasData}", method = RequestMethod.GET)
@ResponseBody
public PageData<FirmwareInfo> getFirmwares(@PathVariable("hasData") boolean hasData,
public PageData<FirmwareInfo> getFirmwares(@PathVariable("deviceProfileId") String strDeviceProfileId,
@PathVariable("type") String strType,
@PathVariable("hasData") boolean hasData,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("deviceProfileId", strDeviceProfileId);
checkParameter("type", strType);
try {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
return checkNotNull(firmwareService.findTenantFirmwaresByTenantIdAndHasData(getTenantId(), hasData, pageLink));
return checkNotNull(firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(getTenantId(),
new DeviceProfileId(toUUID(strDeviceProfileId)), FirmwareType.valueOf(strType), hasData, pageLink));
} catch (Exception e) {
throw handleException(e);
}

4
application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java

@ -47,9 +47,9 @@ public class Lwm2mController extends BaseController {
@RequestMapping(value = "/lwm2m/deviceProfile/bootstrap/{securityMode}/{bootstrapServerIs}", method = RequestMethod.GET)
@ResponseBody
public ServerSecurityConfig getLwm2mBootstrapSecurityInfo(@PathVariable("securityMode") String securityMode,
@PathVariable("bootstrapServerIs") boolean bootstrapServerIs) throws ThingsboardException {
@PathVariable("bootstrapServerIs") boolean bootstrapServer) throws ThingsboardException {
try {
return lwM2MModelsRepository.getBootstrapSecurityInfo(securityMode, bootstrapServerIs);
return lwM2MServerSecurityInfoRepository.getServerSecurityInfo(securityMode, bootstrapServer);
} catch (Exception e) {
throw handleException(e);
}

24
application/src/main/java/org/thingsboard/server/controller/UserController.java

@ -32,6 +32,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User;
@ -89,13 +90,14 @@ public class UserController extends BaseController {
try {
UserId userId = new UserId(toUUID(strUserId));
User user = checkUserId(userId, Operation.READ);
if(!user.getAdditionalInfo().isNull()) {
processDashboardIdFromAdditionalInfo((ObjectNode) user.getAdditionalInfo(), DEFAULT_DASHBOARD);
processDashboardIdFromAdditionalInfo((ObjectNode) user.getAdditionalInfo(), HOME_DASHBOARD);
}
UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId());
if(userCredentials.isEnabled()) {
addUserCredentialsEnabled((ObjectNode) user.getAdditionalInfo());
if(user.getAdditionalInfo().isObject()) {
ObjectNode additionalInfo = (ObjectNode) user.getAdditionalInfo();
processDashboardIdFromAdditionalInfo(additionalInfo, DEFAULT_DASHBOARD);
processDashboardIdFromAdditionalInfo(additionalInfo, HOME_DASHBOARD);
UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId());
if(userCredentials.isEnabled() && !additionalInfo.has("userCredentialsEnabled")) {
additionalInfo.put("userCredentialsEnabled", true);
}
}
return user;
} catch (Exception e) {
@ -103,14 +105,6 @@ public class UserController extends BaseController {
}
}
private void addUserCredentialsEnabled(ObjectNode additionalInfo) {
if(!additionalInfo.isNull()) {
if(!additionalInfo.has("userCredentialsEnabled")) {
additionalInfo.put("userCredentialsEnabled", true);
}
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/user/tokenAccessEnabled", method = RequestMethod.GET)
@ResponseBody

141
application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java

@ -19,13 +19,17 @@ import com.google.common.util.concurrent.FutureCallback;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.firmware.FirmwareUtil;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKey;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
@ -43,37 +47,49 @@ import org.thingsboard.server.queue.TbQueueProducer;
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.queue.provider.TbCoreQueueFactory;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.queue.TbClusterService;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer;
import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_CHECKSUM;
import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_CHECKSUM_ALGORITHM;
import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_SIZE;
import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_TITLE;
import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_VERSION;
import java.util.function.Function;
import static org.thingsboard.server.common.data.firmware.FirmwareKey.CHECKSUM;
import static org.thingsboard.server.common.data.firmware.FirmwareKey.CHECKSUM_ALGORITHM;
import static org.thingsboard.server.common.data.firmware.FirmwareKey.SIZE;
import static org.thingsboard.server.common.data.firmware.FirmwareKey.STATE;
import static org.thingsboard.server.common.data.firmware.FirmwareKey.TITLE;
import static org.thingsboard.server.common.data.firmware.FirmwareKey.TS;
import static org.thingsboard.server.common.data.firmware.FirmwareKey.VERSION;
import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getAttributeKey;
import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getTargetTelemetryKey;
import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getTelemetryKey;
import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE;
@Slf4j
@Service
@TbCoreComponent
public class DefaultFirmwareStateService implements FirmwareStateService {
private final TbClusterService tbClusterService;
private final FirmwareService firmwareService;
private final DeviceService deviceService;
private final DeviceProfileService deviceProfileService;
private final RuleEngineTelemetryService telemetryService;
private final TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> fwStateMsgProducer;
public DefaultFirmwareStateService(FirmwareService firmwareService,
public DefaultFirmwareStateService(TbClusterService tbClusterService, FirmwareService firmwareService,
DeviceService deviceService,
DeviceProfileService deviceProfileService,
RuleEngineTelemetryService telemetryService,
TbCoreQueueFactory coreQueueFactory) {
this.tbClusterService = tbClusterService;
this.firmwareService = firmwareService;
this.deviceService = deviceService;
this.deviceProfileService = deviceProfileService;
@ -83,6 +99,11 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
@Override
public void update(Device device, Device oldDevice) {
updateFirmware(device, oldDevice);
updateSoftware(device, oldDevice);
}
private void updateFirmware(Device device, Device oldDevice) {
FirmwareId newFirmwareId = device.getFirmwareId();
if (newFirmwareId == null) {
DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId());
@ -97,35 +118,84 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
}
if (!newFirmwareId.equals(oldFirmwareId)) {
// Device was updated and new firmware is different from previous firmware.
send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis());
send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis(), FIRMWARE);
}
} else {
// Device was updated and new firmware is not set.
remove(device);
remove(device, FIRMWARE);
}
} else if (newFirmwareId != null) {
// Device was created and firmware is defined.
send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis());
send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis(), FIRMWARE);
}
}
private void updateSoftware(Device device, Device oldDevice) {
FirmwareId newSoftwareId = device.getSoftwareId();
if (newSoftwareId == null) {
DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId());
newSoftwareId = newDeviceProfile.getSoftwareId();
}
if (oldDevice != null) {
if (newSoftwareId != null) {
FirmwareId oldSoftwareId = oldDevice.getSoftwareId();
if (oldSoftwareId == null) {
DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(oldDevice.getTenantId(), oldDevice.getDeviceProfileId());
oldSoftwareId = oldDeviceProfile.getSoftwareId();
}
if (!newSoftwareId.equals(oldSoftwareId)) {
// Device was updated and new firmware is different from previous firmware.
send(device.getTenantId(), device.getId(), newSoftwareId, System.currentTimeMillis(), SOFTWARE);
}
} else {
// Device was updated and new firmware is not set.
remove(device, SOFTWARE);
}
} else if (newSoftwareId != null) {
// Device was created and firmware is defined.
send(device.getTenantId(), device.getId(), newSoftwareId, System.currentTimeMillis(), SOFTWARE);
}
}
@Override
public void update(DeviceProfile deviceProfile) {
public void update(DeviceProfile deviceProfile, boolean isFirmwareChanged, boolean isSoftwareChanged) {
TenantId tenantId = deviceProfile.getTenantId();
if (isFirmwareChanged) {
update(tenantId, deviceProfile, FIRMWARE);
}
if (isSoftwareChanged) {
update(tenantId, deviceProfile, SOFTWARE);
}
}
private void update(TenantId tenantId, DeviceProfile deviceProfile, FirmwareType firmwareType) {
Function<PageLink, PageData<Device>> getDevicesFunction;
Consumer<Device> updateConsumer;
switch (firmwareType) {
case FIRMWARE:
getDevicesFunction = pl -> deviceService.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId, deviceProfile.getName(), pl);
break;
case SOFTWARE:
getDevicesFunction = pl -> deviceService.findDevicesByTenantIdAndTypeAndEmptySoftware(tenantId, deviceProfile.getName(), pl);
break;
default:
log.warn("Unsupported firmware type: [{}]", firmwareType);
return;
}
if (deviceProfile.getFirmwareId() != null) {
long ts = System.currentTimeMillis();
updateConsumer = d -> send(d.getTenantId(), d.getId(), deviceProfile.getFirmwareId(), ts);
updateConsumer = d -> send(d.getTenantId(), d.getId(), deviceProfile.getFirmwareId(), ts, firmwareType);
} else {
updateConsumer = this::remove;
updateConsumer = d -> remove(d, firmwareType);
}
PageLink pageLink = new PageLink(100);
PageData<Device> pageData;
do {
pageData = deviceService.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId, deviceProfile.getName(), pageLink);
pageData = getDevicesFunction.apply(pageLink);
pageData.getData().forEach(updateConsumer);
if (pageData.hasNext()) {
@ -140,16 +210,17 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
FirmwareId targetFirmwareId = new FirmwareId(new UUID(msg.getFirmwareIdMSB(), msg.getFirmwareIdLSB()));
DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB()));
TenantId tenantId = new TenantId(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB()));
FirmwareType firmwareType = FirmwareType.valueOf(msg.getType());
long ts = msg.getTs();
Device device = deviceService.findDeviceById(tenantId, deviceId);
if (device == null) {
log.warn("[{}] [{}] Device was removed during firmware update msg was queued!", tenantId, deviceId);
} else {
FirmwareId currentFirmwareId = device.getFirmwareId();
FirmwareId currentFirmwareId = FirmwareUtil.getFirmwareId(device, firmwareType);
if (currentFirmwareId == null) {
currentFirmwareId = deviceProfileService.findDeviceProfileById(tenantId, device.getDeviceProfileId()).getFirmwareId();
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(tenantId, device.getDeviceProfileId());
currentFirmwareId = FirmwareUtil.getFirmwareId(deviceProfile, firmwareType);
}
if (targetFirmwareId.equals(currentFirmwareId)) {
@ -162,7 +233,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
return isSuccess;
}
private void send(TenantId tenantId, DeviceId deviceId, FirmwareId firmwareId, long ts) {
private void send(TenantId tenantId, DeviceId deviceId, FirmwareId firmwareId, long ts, FirmwareType firmwareType) {
ToFirmwareStateServiceMsg msg = ToFirmwareStateServiceMsg.newBuilder()
.setTenantIdMSB(tenantId.getId().getMostSignificantBits())
.setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
@ -170,6 +241,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
.setDeviceIdLSB(deviceId.getId().getLeastSignificantBits())
.setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits())
.setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits())
.setType(firmwareType.name())
.setTs(ts)
.build();
@ -183,10 +255,10 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
fwStateMsgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), null);
List<TsKvEntry> telemetry = new ArrayList<>();
telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_TITLE, firmware.getTitle())));
telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_VERSION, firmware.getVersion())));
telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(DataConstants.TARGET_FIRMWARE_TS, ts)));
telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_STATE, FirmwareUpdateStatus.QUEUED.name())));
telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), TITLE), firmware.getTitle())));
telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), VERSION), firmware.getVersion())));
telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(getTargetTelemetryKey(firmware.getType(), TS), ts)));
telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), FirmwareUpdateStatus.QUEUED.name())));
telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() {
@Override
@ -206,7 +278,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
TenantId tenantId = device.getTenantId();
DeviceId deviceId = device.getId();
BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(DataConstants.FIRMWARE_STATE, FirmwareUpdateStatus.INITIATED.name()));
BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), FirmwareUpdateStatus.INITIATED.name()));
telemetryService.saveAndNotify(tenantId, deviceId, Collections.singletonList(status), new FutureCallback<>() {
@Override
@ -221,13 +293,12 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
});
List<AttributeKvEntry> attributes = new ArrayList<>();
attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), TITLE), firmware.getTitle())));
attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), VERSION), firmware.getVersion())));
attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(getAttributeKey(firmware.getType(), SIZE), firmware.getDataSize())));
attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), CHECKSUM_ALGORITHM), firmware.getChecksumAlgorithm())));
attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), CHECKSUM), firmware.getChecksum())));
attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_TITLE, firmware.getTitle())));
attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_VERSION, firmware.getVersion())));
attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(FIRMWARE_SIZE, firmware.getDataSize())));
attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_CHECKSUM_ALGORITHM, firmware.getChecksumAlgorithm())));
attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_CHECKSUM, firmware.getChecksum())));
telemetryService.saveAndNotify(tenantId, deviceId, DataConstants.SHARED_SCOPE, attributes, new FutureCallback<>() {
@Override
public void onSuccess(@Nullable Void tmp) {
@ -241,13 +312,15 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
});
}
private void remove(Device device) {
telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE,
Arrays.asList(FIRMWARE_TITLE, FIRMWARE_VERSION, FIRMWARE_SIZE, FIRMWARE_CHECKSUM_ALGORITHM, FIRMWARE_CHECKSUM),
private void remove(Device device, FirmwareType firmwareType) {
telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, FirmwareUtil.getAttributeKeys(firmwareType),
new FutureCallback<>() {
@Override
public void onSuccess(@Nullable Void tmp) {
log.trace("[{}] Success remove target firmware attributes!", device.getId());
Set<AttributeKey> keysToNotify = new HashSet<>();
FirmwareUtil.ALL_FW_ATTRIBUTE_KEYS.forEach(key -> keysToNotify.add(new AttributeKey(DataConstants.SHARED_SCOPE, key)));
tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(device.getTenantId(), device.getId(), keysToNotify), null);
}
@Override

2
application/src/main/java/org/thingsboard/server/service/firmware/FirmwareStateService.java

@ -23,7 +23,7 @@ public interface FirmwareStateService {
void update(Device device, Device oldDevice);
void update(DeviceProfile deviceProfile);
void update(DeviceProfile deviceProfile, boolean isFirmwareChanged, boolean isSoftwareChanged);
boolean process(ToFirmwareStateServiceMsg msg);

113
application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java → application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java

@ -16,14 +16,15 @@
package org.thingsboard.server.service.lwm2m;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.util.Hex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig;
import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigBootstrap;
import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigServer;
import org.thingsboard.server.transport.lwm2m.config.LwM2MSecureServerConfig;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
import java.math.BigInteger;
@ -42,96 +43,59 @@ import java.security.spec.KeySpec;
@Slf4j
@Service
@RequiredArgsConstructor
@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true') || '${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'")
public class LwM2MModelsRepository {
public class LwM2MServerSecurityInfoRepository {
private static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
@Autowired
LwM2MTransportConfigServer contextServer;
@Autowired
LwM2MTransportConfigBootstrap contextBootStrap;
private final LwM2MTransportServerConfig serverConfig;
private final LwM2MTransportBootstrapConfig bootstrapConfig;
/**
* @param securityMode
* @param bootstrapServerIs
* @param bootstrapServer
* @return ServerSecurityConfig more value is default: Important - port, host, publicKey
*/
public ServerSecurityConfig getBootstrapSecurityInfo(String securityMode, boolean bootstrapServerIs) {
public ServerSecurityConfig getServerSecurityInfo(String securityMode, boolean bootstrapServer) {
LwM2MSecurityMode lwM2MSecurityMode = LwM2MSecurityMode.fromSecurityMode(securityMode.toLowerCase());
return getBootstrapServer(bootstrapServerIs, lwM2MSecurityMode);
ServerSecurityConfig result = getServerSecurityConfig(bootstrapServer ? bootstrapConfig : serverConfig, lwM2MSecurityMode);
result.setBootstrapServerIs(bootstrapServer);
return result;
}
/**
* @param bootstrapServerIs
* @param mode
* @return ServerSecurityConfig more value is default: Important - port, host, publicKey
*/
private ServerSecurityConfig getBootstrapServer(boolean bootstrapServerIs, LwM2MSecurityMode mode) {
private ServerSecurityConfig getServerSecurityConfig(LwM2MSecureServerConfig serverConfig, LwM2MSecurityMode mode) {
ServerSecurityConfig bsServ = new ServerSecurityConfig();
bsServ.setBootstrapServerIs(bootstrapServerIs);
if (bootstrapServerIs) {
bsServ.setServerId(contextBootStrap.getBootstrapServerId());
switch (mode) {
case NO_SEC:
bsServ.setHost(contextBootStrap.getBootstrapHost());
bsServ.setPort(contextBootStrap.getBootstrapPortNoSec());
bsServ.setServerPublicKey("");
break;
case PSK:
bsServ.setHost(contextBootStrap.getBootstrapHostSecurity());
bsServ.setPort(contextBootStrap.getBootstrapPortSecurity());
bsServ.setServerPublicKey("");
break;
case RPK:
case X509:
bsServ.setHost(contextBootStrap.getBootstrapHostSecurity());
bsServ.setPort(contextBootStrap.getBootstrapPortSecurity());
bsServ.setServerPublicKey(getPublicKey (contextBootStrap.getBootstrapAlias(), this.contextBootStrap.getBootstrapPublicX(), this.contextBootStrap.getBootstrapPublicY()));
break;
default:
break;
}
} else {
bsServ.setServerId(contextServer.getServerId());
switch (mode) {
case NO_SEC:
bsServ.setHost(contextServer.getServerHost());
bsServ.setPort(contextServer.getServerPortNoSec());
bsServ.setServerPublicKey("");
break;
case PSK:
bsServ.setHost(contextServer.getServerHostSecurity());
bsServ.setPort(contextServer.getServerPortSecurity());
bsServ.setServerPublicKey("");
break;
case RPK:
case X509:
bsServ.setHost(contextServer.getServerHostSecurity());
bsServ.setPort(contextServer.getServerPortSecurity());
bsServ.setServerPublicKey(getPublicKey (contextServer.getServerAlias(), this.contextServer.getServerPublicX(), this.contextServer.getServerPublicY()));
break;
default:
break;
}
bsServ.setServerId(serverConfig.getId());
switch (mode) {
case NO_SEC:
bsServ.setHost(serverConfig.getHost());
bsServ.setPort(serverConfig.getPort());
bsServ.setServerPublicKey("");
break;
case PSK:
bsServ.setHost(serverConfig.getSecureHost());
bsServ.setPort(serverConfig.getSecurePort());
bsServ.setServerPublicKey("");
break;
case RPK:
case X509:
bsServ.setHost(serverConfig.getSecureHost());
bsServ.setPort(serverConfig.getSecurePort());
bsServ.setServerPublicKey(getPublicKey(serverConfig.getCertificateAlias(), this.serverConfig.getPublicX(), this.serverConfig.getPublicY()));
break;
default:
break;
}
return bsServ;
}
private String getPublicKey (String alias, String publicServerX, String publicServerY) {
private String getPublicKey(String alias, String publicServerX, String publicServerY) {
String publicKey = getServerPublicKeyX509(alias);
return publicKey != null ? publicKey : getRPKPublicKey(publicServerX, publicServerY);
}
/**
* @param alias
* @return PublicKey format HexString or null
*/
private String getServerPublicKeyX509(String alias) {
try {
X509Certificate serverCertificate = (X509Certificate) contextServer.getKeyStoreValue().getCertificate(alias);
X509Certificate serverCertificate = (X509Certificate) serverConfig.getKeyStoreValue().getCertificate(alias);
return Hex.encodeHexString(serverCertificate.getEncoded());
} catch (CertificateEncodingException | KeyStoreException e) {
e.printStackTrace();
@ -139,11 +103,6 @@ public class LwM2MModelsRepository {
return null;
}
/**
* @param publicServerX
* @param publicServerY
* @return PublicKey format HexString or null
*/
private String getRPKPublicKey(String publicServerX, String publicServerY) {
try {
/** Get Elliptic Curve Parameter spec for secp256r1 */

2
application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java

@ -185,7 +185,7 @@ public class DefaultTbResourceService implements TbResourceService {
instance.setId(0);
List<LwM2mResourceObserve> resources = new ArrayList<>();
obj.resources.forEach((k, v) -> {
if (!v.operations.isExecutable()) {
if (v.operations.isReadable()) {
LwM2mResourceObserve lwM2MResourceObserve = new LwM2mResourceObserve(k, v.name, false, false, false);
resources.add(lwM2MResourceObserve);
}

10
application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java

@ -41,6 +41,8 @@ import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData;
import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.firmware.FirmwareUtil;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
@ -512,16 +514,17 @@ public class DefaultTransportApiService implements TransportApiService {
private ListenableFuture<TransportApiResponseMsg> handle(TransportProtos.GetFirmwareRequestMsg requestMsg) {
TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB()));
DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB()));
FirmwareType firmwareType = FirmwareType.valueOf(requestMsg.getType());
Device device = deviceService.findDeviceById(tenantId, deviceId);
if (device == null) {
return getEmptyTransportApiResponseFuture();
}
FirmwareId firmwareId = device.getFirmwareId();
FirmwareId firmwareId = FirmwareUtil.getFirmwareId(device, firmwareType);
if (firmwareId == null) {
firmwareId = deviceProfileCache.find(device.getDeviceProfileId()).getFirmwareId();
DeviceProfile deviceProfile = deviceProfileCache.find(device.getDeviceProfileId());
firmwareId = FirmwareUtil.getFirmwareId(deviceProfile, firmwareType);
}
TransportProtos.GetFirmwareResponseMsg.Builder builder = TransportProtos.GetFirmwareResponseMsg.newBuilder();
@ -537,6 +540,7 @@ public class DefaultTransportApiService implements TransportApiService {
builder.setResponseStatus(TransportProtos.ResponseStatus.SUCCESS);
builder.setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits());
builder.setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits());
builder.setType(firmwareInfo.getType().name());
builder.setTitle(firmwareInfo.getTitle());
builder.setVersion(firmwareInfo.getVersion());
builder.setFileName(firmwareInfo.getFileName());

61
application/src/main/resources/thingsboard.yml

@ -630,38 +630,14 @@ transport:
lwm2m:
# Enable/disable lvm2m transport protocol.
enabled: "${LWM2M_ENABLED:true}"
# We choose a default timeout a bit higher to the MAX_TRANSMIT_WAIT(62-93s) which is the time from starting to
# send a Confirmable message to the time when an acknowledgement is no longer expected.
# DEFAULT_TIMEOUT = 2 * 60 * 1000l; 2 min in ms
timeout: "${LWM2M_TIMEOUT:120000}"
recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}"
recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:true}"
response_pool_size: "${LWM2M_RESPONSE_POOL_SIZE:100}"
registered_pool_size: "${LWM2M_REGISTERED_POOL_SIZE:10}"
update_registered_pool_size: "${LWM2M_UPDATE_REGISTERED_POOL_SIZE:10}"
un_registered_pool_size: "${LWM2M_UN_REGISTERED_POOL_SIZE:10}"
secure:
# Certificate_x509:
# To get helps about files format and how to generate it, see: https://github.com/eclipse/leshan/wiki/Credential-files-format
# Create new X509 Certificates: common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh
key_store_type: "${LWM2M_KEYSTORE_TYPE:JKS}"
# key_store_path_file: "${KEY_STORE_PATH_FILE:/common/transport/lwm2m/src/main/resources/credentials/serverKeyStore.jks"
key_store_path_file: "${KEY_STORE_PATH_FILE:}"
key_store_password: "${LWM2M_KEYSTORE_PASSWORD_SERVER:server_ks_password}"
root_alias: "${LWM2M_SERVER_ROOT_CA:rootca}"
enable_gen_new_key_psk_rpk: "${ENABLE_GEN_NEW_KEY_PSK_RPK:false}"
server:
id: "${LWM2M_SERVER_ID:123}"
bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}"
bind_port_no_sec: "${LWM2M_BIND_PORT_NO_SEC:5685}"
secure:
bind_address_security: "${LWM2M_BIND_ADDRESS_SECURITY:0.0.0.0}"
bind_port_security: "${LWM2M_BIND_PORT_SECURITY:5686}"
# create_rpk: "${CREATE_RPK:}"
bind_port: "${LWM2M_BIND_PORT:5685}"
security:
bind_address: "${LWM2M_BIND_ADDRESS_SECURITY:0.0.0.0}"
bind_port: "${LWM2M_BIND_PORT_SECURITY:5686}"
# Only for RPK: Public & Private Key. If the keystore file is missing or not working
# - Public Key (Hex): [3059301306072a8648ce3d020106082a8648ce3d0301070342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b]
# - Private Key (Hex): [308193020100301306072a8648ce3d020106082a8648ce3d030107047930770201010420dc774b309e547ceb48fee547e104ce201a9c48c449dc5414cd04e7f5cf05f67ba00a06082a8648ce3d030107a1440342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b],
# - Elliptic Curve parameters : [secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)]
public_x: "${LWM2M_SERVER_PUBLIC_X:05064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f358}"
public_y: "${LWM2M_SERVER_PUBLIC_Y:5eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b}"
private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED:308193020100301306072a8648ce3d020106082a8648ce3d030107047930770201010420dc774b309e547ceb48fee547e104ce201a9c48c449dc5414cd04e7f5cf05f67ba00a06082a8648ce3d030107a1440342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b}"
@ -671,19 +647,34 @@ transport:
enable: "${LWM2M_ENABLED_BS:true}"
id: "${LWM2M_SERVER_ID_BS:111}"
bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}"
bind_port_no_sec: "${LWM2M_BIND_PORT_NO_SEC_BS:5687}"
secure:
bind_address_security: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}"
bind_port_security: "${LWM2M_BIND_PORT_SECURITY_BS:5688}"
bind_port: "${LWM2M_BIND_PORT_BS:5687}"
security:
bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}"
bind_port: "${LWM2M_BIND_PORT_SECURITY_BS:5688}"
# Only for RPK: Public & Private Key. If the keystore file is missing or not working
# - Elliptic Curve parameters : [secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)]
# - Public Key (Hex): [3059301306072a8648ce3d020106082a8648ce3d030107034200045017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f913fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34]
# - Private Key (Hex): [308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104205ecafd90caa7be45c42e1f3f32571632b8409e6e6249d7124f4ba56fab3c8083a00a06082a8648ce3d030107a144034200045017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f913fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34],
public_x: "${LWM2M_SERVER_PUBLIC_X_BS:5017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f91}"
public_y: "${LWM2M_SERVER_PUBLIC_Y_BS:3fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34}"
private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED_BS:308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104205ecafd90caa7be45c42e1f3f32571632b8409e6e6249d7124f4ba56fab3c8083a00a06082a8648ce3d030107a144034200045017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f913fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34}"
# Only Certificate_x509:
alias: "${LWM2M_KEYSTORE_ALIAS_BS:bootstrap}"
security:
# Certificate_x509:
# To get helps about files format and how to generate it, see: https://github.com/eclipse/leshan/wiki/Credential-files-format
# Create new X509 Certificates: common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh
key_store_type: "${LWM2M_KEYSTORE_TYPE:JKS}"
# key_store_path_file: "${KEY_STORE_PATH_FILE:/common/transport/lwm2m/src/main/resources/credentials/serverKeyStore.jks"
key_store: "${LWM2M_KEY_STORE:lwm2mserver.jks}"
key_store_password: "${LWM2M_KEY_STORE_PASSWORD:server_ks_password}"
root_alias: "${LWM2M_SERVER_ROOT_CA:rootca}"
enable_gen_new_key_psk_rpk: "${ENABLE_GEN_NEW_KEY_PSK_RPK:false}"
timeout: "${LWM2M_TIMEOUT:120000}"
recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}"
recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:true}"
response_pool_size: "${LWM2M_RESPONSE_POOL_SIZE:100}"
registered_pool_size: "${LWM2M_REGISTERED_POOL_SIZE:10}"
update_registered_pool_size: "${LWM2M_UPDATE_REGISTERED_POOL_SIZE:10}"
un_registered_pool_size: "${LWM2M_UN_REGISTERED_POOL_SIZE:10}"
log_max_length: "${LWM2M_LOG_MAX_LENGTH:100}"
# Use redis for Security and Registration stores
redis.enabled: "${LWM2M_REDIS_ENABLED:false}"
snmp:

30
application/src/test/java/org/thingsboard/server/controller/BaseFirmwareControllerTest.java

@ -24,10 +24,13 @@ import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.Firmware;
import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority;
@ -38,6 +41,7 @@ import java.util.Collections;
import java.util.List;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
public abstract class BaseFirmwareControllerTest extends AbstractControllerTest {
@ -53,6 +57,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
private Tenant savedTenant;
private User tenantAdmin;
private DeviceProfileId deviceProfileId;
@Before
public void beforeTest() throws Exception {
@ -71,6 +76,11 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
tenantAdmin.setLastName("Downs");
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null);
DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
Assert.assertNotNull(savedDeviceProfile);
deviceProfileId = savedDeviceProfile.getId();
}
@After
@ -84,6 +94,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
@Test
public void testSaveFirmware() throws Exception {
FirmwareInfo firmwareInfo = new FirmwareInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION);
@ -107,6 +119,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
@Test
public void testSaveFirmwareData() throws Exception {
FirmwareInfo firmwareInfo = new FirmwareInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION);
@ -137,6 +151,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
@Test
public void testUpdateFirmwareFromDifferentTenant() throws Exception {
FirmwareInfo firmwareInfo = new FirmwareInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION);
@ -150,6 +166,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
@Test
public void testFindFirmwareInfoById() throws Exception {
FirmwareInfo firmwareInfo = new FirmwareInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION);
@ -163,6 +181,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
@Test
public void testFindFirmwareById() throws Exception {
FirmwareInfo firmwareInfo = new FirmwareInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION);
@ -180,6 +200,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
@Test
public void testDeleteFirmware() throws Exception {
FirmwareInfo firmwareInfo = new FirmwareInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION);
@ -197,6 +219,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
List<FirmwareInfo> firmwares = new ArrayList<>();
for (int i = 0; i < 165; i++) {
FirmwareInfo firmwareInfo = new FirmwareInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION + i);
@ -238,6 +262,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
for (int i = 0; i < 165; i++) {
FirmwareInfo firmwareInfo = new FirmwareInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION + i);
@ -257,7 +283,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
PageLink pageLink = new PageLink(24);
PageData<FirmwareInfo> pageData;
do {
pageData = doGetTypedWithPageLink("/api/firmwares/true?",
pageData = doGetTypedWithPageLink("/api/firmwares/" + deviceProfileId.toString() + "/FIRMWARE/true?",
new TypeReference<>() {
}, pageLink);
loadedFirmwaresWithData.addAll(pageData.getData());
@ -269,7 +295,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
List<FirmwareInfo> loadedFirmwaresWithoutData = new ArrayList<>();
pageLink = new PageLink(24);
do {
pageData = doGetTypedWithPageLink("/api/firmwares/false?",
pageData = doGetTypedWithPageLink("/api/firmwares/" + deviceProfileId.toString() + "/FIRMWARE/false?",
new TypeReference<>() {
}, pageLink);
loadedFirmwaresWithoutData.addAll(pageData.getData());

2
common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java

@ -65,6 +65,8 @@ public interface DeviceService {
PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink);
PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(TenantId tenantId, String type, PageLink pageLink);
PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink);
PageData<DeviceInfo> findDeviceInfosByTenantIdAndDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId, PageLink pageLink);

4
common/dao-api/src/main/java/org/thingsboard/server/dao/firmware/FirmwareService.java

@ -18,6 +18,8 @@ package org.thingsboard.server.dao.firmware;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.Firmware;
import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
@ -37,7 +39,7 @@ public interface FirmwareService {
PageData<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink);
PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink);
PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink);
void deleteFirmware(TenantId tenantId, FirmwareId firmwareId);

36
common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java

@ -93,22 +93,26 @@ public class DataConstants {
public static final String USERNAME = "username";
public static final String PASSWORD = "password";
//firmware
//telemetry
public static final String CURRENT_FIRMWARE_TITLE = "cur_fw_title";
public static final String CURRENT_FIRMWARE_VERSION = "cur_fw_version";
public static final String TARGET_FIRMWARE_TITLE = "target_fw_title";
public static final String TARGET_FIRMWARE_VERSION = "target_fw_version";
public static final String TARGET_FIRMWARE_TS = "target_fw_ts";
public static final String FIRMWARE_STATE = "fw_state";
//attributes
//telemetry
public static final String FIRMWARE_TITLE = "fw_title";
public static final String FIRMWARE_VERSION = "fw_version";
public static final String FIRMWARE_SIZE = "fw_size";
public static final String FIRMWARE_CHECKSUM = "fw_checksum";
public static final String FIRMWARE_CHECKSUM_ALGORITHM = "fw_checksum_algorithm";
//<<<<<<< HEAD
//=======
// //firmware
// //telemetry
// public static final String CURRENT_FIRMWARE_TITLE = "current_fw_title";
// public static final String CURRENT_FIRMWARE_VERSION = "current_fw_version";
// public static final String TARGET_FIRMWARE_TITLE = "target_fw_title";
// public static final String TARGET_FIRMWARE_VERSION = "target_fw_version";
// public static final String TARGET_FIRMWARE_TS = "target_fw_ts";
// public static final String FIRMWARE_STATE = "fw_state";
//
// //attributes
// //telemetry
// public static final String FIRMWARE_TITLE = "fw_title";
// public static final String FIRMWARE_VERSION = "fw_version";
// public static final String FIRMWARE_SIZE = "fw_size";
// public static final String FIRMWARE_CHECKSUM = "fw_checksum";
// public static final String FIRMWARE_CHECKSUM_ALGORITHM = "fw_checksum_algorithm";
//>>>>>>> origin/master
public static final String EDGE_MSG_SOURCE = "edge";
public static final String MSG_SOURCE_KEY = "source";
}

14
common/data/src/main/java/org/thingsboard/server/common/data/Device.java

@ -32,7 +32,7 @@ import java.io.IOException;
@EqualsAndHashCode(callSuper = true)
@Slf4j
public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implements HasName, HasTenantId, HasCustomerId {
public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implements HasName, HasTenantId, HasCustomerId, HasFirmware {
private static final long serialVersionUID = 2807343040519543363L;
@ -50,6 +50,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
private byte[] deviceDataBytes;
private FirmwareId firmwareId;
private FirmwareId softwareId;
public Device() {
super();
@ -69,6 +70,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
this.deviceProfileId = device.getDeviceProfileId();
this.setDeviceData(device.getDeviceData());
this.firmwareId = device.getFirmwareId();
this.softwareId = device.getSoftwareId();
}
public Device updateDevice(Device device) {
@ -79,6 +81,8 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
this.label = device.getLabel();
this.deviceProfileId = device.getDeviceProfileId();
this.setDeviceData(device.getDeviceData());
this.setFirmwareId(device.getFirmwareId());
this.setSoftwareId(device.getSoftwareId());
return this;
}
@ -171,6 +175,14 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
this.firmwareId = firmwareId;
}
public FirmwareId getSoftwareId() {
return softwareId;
}
public void setSoftwareId(FirmwareId softwareId) {
this.softwareId = softwareId;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();

6
common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java

@ -36,7 +36,7 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn
@Data
@EqualsAndHashCode(callSuper = true)
@Slf4j
public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements HasName, HasTenantId {
public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements HasName, HasTenantId, HasFirmware {
private TenantId tenantId;
@NoXss
@ -59,6 +59,8 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
private FirmwareId firmwareId;
private FirmwareId softwareId;
public DeviceProfile() {
super();
}
@ -77,6 +79,8 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
this.defaultQueueName = deviceProfile.getDefaultQueueName();
this.setProfileData(deviceProfile.getProfileData());
this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey();
this.firmwareId = deviceProfile.getFirmwareId();
this.softwareId = deviceProfile.getSoftwareId();
}
@Override

6
common/data/src/main/java/org/thingsboard/server/common/data/FirmwareInfo.java

@ -19,6 +19,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.data.id.TenantId;
@ -30,6 +32,8 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId>
private static final long serialVersionUID = 3168391583570815419L;
private TenantId tenantId;
private DeviceProfileId deviceProfileId;
private FirmwareType type;
private String title;
private String version;
private boolean hasData;
@ -51,6 +55,8 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId>
public FirmwareInfo(FirmwareInfo firmwareInfo) {
super(firmwareInfo);
this.tenantId = firmwareInfo.getTenantId();
this.deviceProfileId = firmwareInfo.getDeviceProfileId();
this.type = firmwareInfo.getType();
this.title = firmwareInfo.getTitle();
this.version = firmwareInfo.getVersion();
this.hasData = firmwareInfo.isHasData();

25
common/data/src/main/java/org/thingsboard/server/common/data/HasFirmware.java

@ -0,0 +1,25 @@
/**
* Copyright © 2016-2021 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.common.data;
import org.thingsboard.server.common.data.id.FirmwareId;
public interface HasFirmware {
FirmwareId getFirmwareId();
FirmwareId getSoftwareId();
}

13
common/data/src/main/java/org/thingsboard/server/common/data/device/profile/MqttTopics.java

@ -31,6 +31,7 @@ public class MqttTopics {
private static final String SUB_TOPIC = "+";
private static final String PROVISION = "/provision";
private static final String FIRMWARE = "/fw";
private static final String SOFTWARE = "/sw";
private static final String CHUNK = "/chunk/";
private static final String ERROR = "/error";
@ -75,9 +76,17 @@ public class MqttTopics {
// v2 topics
public static final String BASE_DEVICE_API_TOPIC_V2 = "v2";
public static final String DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + RESPONSE + "/";
public static final String DEVICE_FIRMWARE_RESPONSES_TOPIC = DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX + SUB_TOPIC + CHUNK + SUB_TOPIC;
public static final String REQUEST_ID_PATTERN = "(?<requestId>\\d+)";
public static final String CHUNK_PATTERN = "(?<chunk>\\d+)";
public static final String DEVICE_FIRMWARE_REQUEST_TOPIC_PATTERN = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + REQUEST + "/" + REQUEST_ID_PATTERN + CHUNK + CHUNK_PATTERN;
public static final String DEVICE_FIRMWARE_RESPONSES_TOPIC = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + RESPONSE + "/" + SUB_TOPIC + CHUNK + SUB_TOPIC;
public static final String DEVICE_FIRMWARE_ERROR_TOPIC = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + ERROR;
public static final String DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT = BASE_DEVICE_API_TOPIC_V2 + "%s" + RESPONSE + "/"+ "%s" + CHUNK + "%d";
public static final String DEVICE_SOFTWARE_REQUEST_TOPIC_PATTERN = BASE_DEVICE_API_TOPIC_V2 + SOFTWARE + REQUEST + "/" + REQUEST_ID_PATTERN + CHUNK + CHUNK_PATTERN;
public static final String DEVICE_SOFTWARE_RESPONSES_TOPIC = BASE_DEVICE_API_TOPIC_V2 + SOFTWARE + RESPONSE + "/" + SUB_TOPIC + CHUNK + SUB_TOPIC;
public static final String DEVICE_SOFTWARE_ERROR_TOPIC = BASE_DEVICE_API_TOPIC_V2 + SOFTWARE + ERROR;
private MqttTopics() {
}

30
common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareKey.java

@ -0,0 +1,30 @@
/**
* Copyright © 2016-2021 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.common.data.firmware;
import lombok.Getter;
public enum FirmwareKey {
TITLE("title"), VERSION("version"), TS("ts"), STATE("state"), SIZE("size"), CHECKSUM("checksum"), CHECKSUM_ALGORITHM("checksum_algorithm");
@Getter
private final String value;
FirmwareKey(String value) {
this.value = value;
}
}

30
common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareType.java

@ -0,0 +1,30 @@
/**
* Copyright © 2016-2021 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.common.data.firmware;
import lombok.Getter;
public enum FirmwareType {
FIRMWARE("fw"), SOFTWARE("sw");
@Getter
private final String keyPrefix;
FirmwareType(String keyPrefix) {
this.keyPrefix = keyPrefix;
}
}

91
common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUtil.java

@ -0,0 +1,91 @@
/**
* Copyright © 2016-2021 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.common.data.firmware;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.HasFirmware;
import org.thingsboard.server.common.data.id.FirmwareId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE;
@Slf4j
public class FirmwareUtil {
public static final List<String> ALL_FW_ATTRIBUTE_KEYS;
public static final List<String> ALL_SW_ATTRIBUTE_KEYS;
static {
ALL_FW_ATTRIBUTE_KEYS = new ArrayList<>();
for (FirmwareKey key : FirmwareKey.values()) {
ALL_FW_ATTRIBUTE_KEYS.add(getAttributeKey(FIRMWARE, key));
}
ALL_SW_ATTRIBUTE_KEYS = new ArrayList<>();
for (FirmwareKey key : FirmwareKey.values()) {
ALL_SW_ATTRIBUTE_KEYS.add(getAttributeKey(SOFTWARE, key));
}
}
public static List<String> getAttributeKeys(FirmwareType firmwareType) {
switch (firmwareType) {
case FIRMWARE:
return ALL_FW_ATTRIBUTE_KEYS;
case SOFTWARE:
return ALL_SW_ATTRIBUTE_KEYS;
}
return Collections.emptyList();
}
public static String getAttributeKey(FirmwareType type, FirmwareKey key) {
return type.getKeyPrefix() + "_" + key.getValue();
}
public static String getTargetTelemetryKey(FirmwareType type, FirmwareKey key) {
return getTelemetryKey("target_", type, key);
}
public static String getCurrentTelemetryKey(FirmwareType type, FirmwareKey key) {
return getTelemetryKey("current_", type, key);
}
private static String getTelemetryKey(String prefix, FirmwareType type, FirmwareKey key) {
return prefix + type.getKeyPrefix() + "_" + key.getValue();
}
public static String getTelemetryKey(FirmwareType type, FirmwareKey key) {
return type.getKeyPrefix() + "_" + key.getValue();
}
public static FirmwareId getFirmwareId(HasFirmware entity, FirmwareType firmwareType) {
switch (firmwareType) {
case FIRMWARE:
return entity.getFirmwareId();
case SOFTWARE:
return entity.getSoftwareId();
default:
log.warn("Unsupported firmware type: [{}]", firmwareType);
return null;
}
}
}

2
common/message/src/main/java/org/thingsboard/server/common/msg/session/FeatureType.java

@ -16,5 +16,5 @@
package org.thingsboard.server.common.msg.session;
public enum FeatureType {
ATTRIBUTES, TELEMETRY, RPC, CLAIM, PROVISION, FIRMWARE
ATTRIBUTES, TELEMETRY, RPC, CLAIM, PROVISION, FIRMWARE, SOFTWARE
}

3
common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java

@ -32,7 +32,8 @@ public enum SessionMsgType {
CLAIM_REQUEST(),
GET_FIRMWARE_REQUEST;
GET_FIRMWARE_REQUEST,
GET_SOFTWARE_REQUEST;
private final boolean requiresRulesProcessing;

11
common/queue/src/main/proto/queue.proto

@ -388,16 +388,18 @@ message GetFirmwareRequestMsg {
int64 deviceIdLSB = 2;
int64 tenantIdMSB = 3;
int64 tenantIdLSB = 4;
string type = 5;
}
message GetFirmwareResponseMsg {
ResponseStatus responseStatus = 1;
int64 firmwareIdMSB = 2;
int64 firmwareIdLSB = 3;
string title = 4;
string version = 5;
string contentType = 6;
string fileName = 7;
string type = 4;
string title = 5;
string version = 6;
string contentType = 7;
string fileName = 8;
}
//Used to report session state to tb-Service and persist this state in the cache on the tb-Service level.
@ -711,4 +713,5 @@ message ToFirmwareStateServiceMsg {
int64 deviceIdLSB = 5;
int64 firmwareIdMSB = 6;
int64 firmwareIdLSB = 7;
string type = 8;
}

23
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java

@ -43,6 +43,7 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportC
import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration;
import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
import org.thingsboard.server.common.msg.session.FeatureType;
import org.thingsboard.server.common.msg.session.SessionMsgType;
@ -122,6 +123,8 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST);
} else if (featureType.get() == FeatureType.FIRMWARE) {
processRequest(exchange, SessionMsgType.GET_FIRMWARE_REQUEST);
} else if (featureType.get() == FeatureType.SOFTWARE) {
processRequest(exchange, SessionMsgType.GET_SOFTWARE_REQUEST);
} else {
log.trace("Invalid feature type parameter");
exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
@ -326,12 +329,10 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
new CoapNoOpCallback(exchange));
break;
case GET_FIRMWARE_REQUEST:
TransportProtos.GetFirmwareRequestMsg requestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
.setTenantIdMSB(sessionInfo.getTenantIdMSB())
.setTenantIdLSB(sessionInfo.getTenantIdLSB())
.setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
.setDeviceIdLSB(sessionInfo.getDeviceIdLSB()).build();
transportContext.getTransportService().process(sessionInfo, requestMsg, new FirmwareCallback(exchange));
getFirmwareCallback(sessionInfo, exchange, FirmwareType.FIRMWARE);
break;
case GET_SOFTWARE_REQUEST:
getFirmwareCallback(sessionInfo, exchange, FirmwareType.SOFTWARE);
break;
}
} catch (AdaptorException e) {
@ -340,6 +341,16 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
}
}
private void getFirmwareCallback(TransportProtos.SessionInfoProto sessionInfo, CoapExchange exchange, FirmwareType firmwareType) {
TransportProtos.GetFirmwareRequestMsg requestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
.setTenantIdMSB(sessionInfo.getTenantIdMSB())
.setTenantIdLSB(sessionInfo.getTenantIdLSB())
.setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
.setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
.setType(firmwareType.name()).build();
transportContext.getTransportService().process(sessionInfo, requestMsg, new FirmwareCallback(exchange));
}
private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(String token) {
tokenToNotificationCounterMap.remove(token);
return tokenToSessionIdMap.remove(token);

37
common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java

@ -34,6 +34,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.TbTransportService;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.transport.SessionMsgListener;
@ -210,8 +211,29 @@ public class DeviceApiController implements TbTransportService {
public DeferredResult<ResponseEntity> getFirmware(@PathVariable("deviceToken") String deviceToken,
@RequestParam(value = "title") String title,
@RequestParam(value = "version") String version,
@RequestParam(value = "chunkSize", required = false, defaultValue = "0") int chunkSize,
@RequestParam(value = "size", required = false, defaultValue = "0") int size,
@RequestParam(value = "chunk", required = false, defaultValue = "0") int chunk) {
return getFirmwareCallback(deviceToken, title, version, size, chunk, FirmwareType.FIRMWARE);
}
@RequestMapping(value = "/{deviceToken}/software", method = RequestMethod.GET)
public DeferredResult<ResponseEntity> getSoftware(@PathVariable("deviceToken") String deviceToken,
@RequestParam(value = "title") String title,
@RequestParam(value = "version") String version,
@RequestParam(value = "size", required = false, defaultValue = "0") int size,
@RequestParam(value = "chunk", required = false, defaultValue = "0") int chunk) {
return getFirmwareCallback(deviceToken, title, version, size, chunk, FirmwareType.SOFTWARE);
}
@RequestMapping(value = "/provision", method = RequestMethod.POST)
public DeferredResult<ResponseEntity> provisionDevice(@RequestBody String json, HttpServletRequest httpRequest) {
DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();
transportContext.getTransportService().process(JsonConverter.convertToProvisionRequestMsg(json),
new DeviceProvisionCallback(responseWriter));
return responseWriter;
}
private DeferredResult<ResponseEntity> getFirmwareCallback(String deviceToken, String title, String version, int size, int chunk, FirmwareType firmwareType) {
DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();
transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
@ -219,20 +241,13 @@ public class DeviceApiController implements TbTransportService {
.setTenantIdMSB(sessionInfo.getTenantIdMSB())
.setTenantIdLSB(sessionInfo.getTenantIdLSB())
.setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
.setDeviceIdLSB(sessionInfo.getDeviceIdLSB()).build();
transportContext.getTransportService().process(sessionInfo, requestMsg, new GetFirmwareCallback(responseWriter, title, version, chunkSize, chunk));
.setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
.setType(firmwareType.name()).build();
transportContext.getTransportService().process(sessionInfo, requestMsg, new GetFirmwareCallback(responseWriter, title, version, size, chunk));
}));
return responseWriter;
}
@RequestMapping(value = "/provision", method = RequestMethod.POST)
public DeferredResult<ResponseEntity> provisionDevice(@RequestBody String json, HttpServletRequest httpRequest) {
DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();
transportContext.getTransportService().process(JsonConverter.convertToProvisionRequestMsg(json),
new DeviceProvisionCallback(responseWriter));
return responseWriter;
}
private static class DeviceAuthCallback implements TransportServiceCallback<ValidateDeviceCredentialsResponse> {
private final TransportContext transportContext;
private final DeferredResult<ResponseEntity> responseWriter;

61
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java

@ -25,10 +25,13 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapSecurityStore;
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MInMemoryBootstrapConfigStore;
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2mDefaultBootstrapSessionManager;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContextServer;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper;
import java.math.BigInteger;
import java.security.AlgorithmParameters;
@ -55,7 +58,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8;
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256;
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getCoapConfig;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig;
@Slf4j
@Component
@ -66,10 +69,10 @@ public class LwM2MTransportBootstrapServerConfiguration {
private boolean pskMode = false;
@Autowired
private LwM2MTransportContextBootstrap contextBs;
private LwM2MTransportServerConfig serverConfig;
@Autowired
private LwM2mTransportContextServer contextS;
private LwM2MTransportContextBootstrap contextBs;
@Autowired
private LwM2MBootstrapSecurityStore lwM2MBootstrapSecurityStore;
@ -81,19 +84,18 @@ public class LwM2MTransportBootstrapServerConfiguration {
@Bean
public LeshanBootstrapServer getLeshanBootstrapServer() {
log.info("Prepare and start BootstrapServer... PostConstruct");
return this.getLhBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPortNoSec(), this.contextBs.getCtxBootStrap().getBootstrapPortSecurity());
return this.getLhBootstrapServer(this.contextBs.getCtxBootStrap().getPort(), this.contextBs.getCtxBootStrap().getSecurePort());
}
public LeshanBootstrapServer getLhBootstrapServer(Integer bootstrapPortNoSec, Integer bootstrapSecurePort) {
LeshanBootstrapServerBuilder builder = new LeshanBootstrapServerBuilder();
builder.setLocalAddress(this.contextBs.getCtxBootStrap().getBootstrapHost(), bootstrapPortNoSec);
builder.setLocalSecureAddress(this.contextBs.getCtxBootStrap().getBootstrapHostSecurity(), bootstrapSecurePort);
builder.setLocalAddress(this.contextBs.getCtxBootStrap().getHost(), bootstrapPortNoSec);
builder.setLocalSecureAddress(this.contextBs.getCtxBootStrap().getSecureHost(), bootstrapSecurePort);
/** Create CoAP Config */
builder.setCoapConfig(getCoapConfig(bootstrapPortNoSec, bootstrapSecurePort));
/** Define model provider (Create Models )*/
// builder.setModel(new StaticModel(contextS.getLwM2MTransportConfigServer().getModelsValueCommon()));
/** Create credentials */
this.setServerWithCredentials(builder);
@ -107,14 +109,13 @@ public class LwM2MTransportBootstrapServerConfiguration {
/** Create and Set DTLS Config */
DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder();
dtlsConfig.setRecommendedSupportedGroupsOnly(this.contextS.getLwM2MTransportConfigServer().isRecommendedSupportedGroups());
dtlsConfig.setRecommendedCipherSuitesOnly(this.contextS.getLwM2MTransportConfigServer().isRecommendedCiphers());
dtlsConfig.setRecommendedSupportedGroupsOnly(serverConfig.isRecommendedSupportedGroups());
dtlsConfig.setRecommendedCipherSuitesOnly(serverConfig.isRecommendedCiphers());
if (this.pskMode) {
dtlsConfig.setSupportedCipherSuites(
TLS_PSK_WITH_AES_128_CCM_8,
TLS_PSK_WITH_AES_128_CBC_SHA256);
}
else {
} else {
dtlsConfig.setSupportedCipherSuites(
TLS_PSK_WITH_AES_128_CCM_8,
TLS_PSK_WITH_AES_128_CBC_SHA256,
@ -134,10 +135,10 @@ public class LwM2MTransportBootstrapServerConfiguration {
private void setServerWithCredentials(LeshanBootstrapServerBuilder builder) {
try {
if (this.contextS.getLwM2MTransportConfigServer().getKeyStoreValue() != null) {
KeyStore keyStoreServer = this.contextS.getLwM2MTransportConfigServer().getKeyStoreValue();
if (serverConfig.getKeyStoreValue() != null) {
KeyStore keyStoreServer = serverConfig.getKeyStoreValue();
if (this.setBuilderX509(builder)) {
X509Certificate rootCAX509Cert = (X509Certificate) keyStoreServer.getCertificate(this.contextS.getLwM2MTransportConfigServer().getRootAlias());
X509Certificate rootCAX509Cert = (X509Certificate) keyStoreServer.getCertificate(serverConfig.getRootCertificateAlias());
if (rootCAX509Cert != null) {
X509Certificate[] trustedCertificates = new X509Certificate[1];
trustedCertificates[0] = rootCAX509Cert;
@ -168,12 +169,10 @@ public class LwM2MTransportBootstrapServerConfiguration {
* For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks
*/
try {
X509Certificate serverCertificate = (X509Certificate) this.contextS.getLwM2MTransportConfigServer().getKeyStoreValue().getCertificate(this.contextBs.getCtxBootStrap().getBootstrapAlias());
PrivateKey privateKey = (PrivateKey) this.contextS.getLwM2MTransportConfigServer().getKeyStoreValue().getKey(this.contextBs.getCtxBootStrap().getBootstrapAlias(), this.contextS.getLwM2MTransportConfigServer().getKeyStorePasswordServer() == null ? null : this.contextS.getLwM2MTransportConfigServer().getKeyStorePasswordServer().toCharArray());
X509Certificate serverCertificate = (X509Certificate) serverConfig.getKeyStoreValue().getCertificate(this.contextBs.getCtxBootStrap().getCertificateAlias());
PrivateKey privateKey = (PrivateKey) serverConfig.getKeyStoreValue().getKey(this.contextBs.getCtxBootStrap().getCertificateAlias(), serverConfig.getKeyStorePassword() == null ? null : serverConfig.getKeyStorePassword().toCharArray());
PublicKey publicKey = serverCertificate.getPublicKey();
if (serverCertificate != null &&
privateKey != null && privateKey.getEncoded().length > 0 &&
publicKey != null && publicKey.getEncoded().length > 0) {
if (privateKey != null && privateKey.getEncoded().length > 0 && publicKey != null && publicKey.getEncoded().length > 0) {
builder.setPublicKey(serverCertificate.getPublicKey());
builder.setPrivateKey(privateKey);
builder.setCertificateChain(new X509Certificate[]{serverCertificate});
@ -200,10 +199,12 @@ public class LwM2MTransportBootstrapServerConfiguration {
}
private void infoPramsUri(String mode) {
log.info("Bootstrap Server uses [{}]: serverNoSecureURI : [{}], serverSecureURI : [{}]",
log.info("Bootstrap Server uses [{}]: serverNoSecureURI : [{}:{}], serverSecureURI : [{}:{}]",
mode,
this.contextBs.getCtxBootStrap().getBootstrapHost() + ":" + this.contextBs.getCtxBootStrap().getBootstrapPortNoSec(),
this.contextBs.getCtxBootStrap().getBootstrapHostSecurity() + ":" + this.contextBs.getCtxBootStrap().getBootstrapPortSecurity());
this.contextBs.getCtxBootStrap().getHost(),
this.contextBs.getCtxBootStrap().getPort(),
this.contextBs.getCtxBootStrap().getSecureHost(),
this.contextBs.getCtxBootStrap().getSecurePort());
}
@ -237,23 +238,25 @@ public class LwM2MTransportBootstrapServerConfiguration {
AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
algoParameters.init(new ECGenParameterSpec("secp256r1"));
ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
if (this.contextBs.getCtxBootStrap().getBootstrapPublicX() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicX().isEmpty() && this.contextBs.getCtxBootStrap().getBootstrapPublicY() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicY().isEmpty()) {
LwM2MTransportBootstrapConfig serverConfig = this.contextBs.getCtxBootStrap();
if (StringUtils.isNotEmpty(serverConfig.getPublicX()) && StringUtils.isNotEmpty(serverConfig.getPublicY())) {
/** Get point values */
byte[] publicX = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicX().toCharArray());
byte[] publicY = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicY().toCharArray());
byte[] publicX = Hex.decodeHex(serverConfig.getPublicX().toCharArray());
byte[] publicY = Hex.decodeHex(serverConfig.getPublicY().toCharArray());
/** Create key specs */
KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
parameterSpec);
/** Get public key */
this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
}
if (this.contextBs.getCtxBootStrap().getBootstrapPrivateEncoded() != null && !this.contextBs.getCtxBootStrap().getBootstrapPrivateEncoded().isEmpty()) {
String privateEncodedKey = serverConfig.getPrivateEncoded();
if (StringUtils.isNotEmpty(privateEncodedKey)) {
/** Get private key */
byte[] privateS = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPrivateEncoded().toCharArray());
byte[] privateS = Hex.decodeHex(privateEncodedKey.toCharArray());
try {
this.privateKey = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(privateS));
} catch (InvalidKeySpecException ignore2) {
log.error("Invalid Bootstrap Server rpk.PrivateKey.getEncoded () [{}}]. PrivateKey has no EC algorithm", this.contextBs.getCtxBootStrap().getBootstrapPrivateEncoded());
log.error("Invalid Bootstrap Server rpk.PrivateKey.getEncoded () [{}}]. PrivateKey has no EC algorithm", privateEncodedKey);
}
}
}

12
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportContextBootstrap.java

@ -34,7 +34,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.transport.TransportContext;
import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigBootstrap;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig;
@Slf4j
@ -42,13 +42,13 @@ import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigBootstr
@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true') || '${service.type:null}'=='monolith'")
public class LwM2MTransportContextBootstrap extends TransportContext {
private final LwM2MTransportConfigBootstrap lwM2MTransportConfigBootstrap;
private final LwM2MTransportBootstrapConfig lwM2MTransportBootstrapConfig;
public LwM2MTransportContextBootstrap(LwM2MTransportConfigBootstrap ctxBootStrap) {
this.lwM2MTransportConfigBootstrap = ctxBootStrap;
public LwM2MTransportContextBootstrap(LwM2MTransportBootstrapConfig ctxBootStrap) {
this.lwM2MTransportBootstrapConfig = ctxBootStrap;
}
public LwM2MTransportConfigBootstrap getCtxBootStrap() {
return this.lwM2MTransportConfigBootstrap;
public LwM2MTransportBootstrapConfig getCtxBootStrap() {
return this.lwM2MTransportBootstrapConfig;
}
}

33
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java

@ -34,8 +34,9 @@ import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore;
import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContextServer;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil;
import java.io.IOException;
import java.security.GeneralSecurityException;
@ -43,12 +44,12 @@ import java.util.Collections;
import java.util.List;
import java.util.UUID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.BOOTSTRAP_SERVER;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LWM2M_SERVER;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.SERVERS;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getBootstrapParametersFromThingsboard;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.BOOTSTRAP_SERVER;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_ERROR;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_INFO;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LWM2M_SERVER;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.SERVERS;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.getBootstrapParametersFromThingsboard;
@Slf4j
@Service("LwM2MBootstrapSecurityStore")
@ -59,17 +60,19 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
private final LwM2mCredentialsSecurityInfoValidator lwM2MCredentialsSecurityInfoValidator;
private final LwM2mTransportContextServer context;
private final LwM2mTransportContext context;
private final LwM2mTransportServerHelper helper;
public LwM2MBootstrapSecurityStore(EditableBootstrapConfigStore bootstrapConfigStore, LwM2mCredentialsSecurityInfoValidator lwM2MCredentialsSecurityInfoValidator, LwM2mTransportContextServer context) {
public LwM2MBootstrapSecurityStore(EditableBootstrapConfigStore bootstrapConfigStore, LwM2mCredentialsSecurityInfoValidator lwM2MCredentialsSecurityInfoValidator, LwM2mTransportContext context, LwM2mTransportServerHelper helper) {
this.bootstrapConfigStore = bootstrapConfigStore;
this.lwM2MCredentialsSecurityInfoValidator = lwM2MCredentialsSecurityInfoValidator;
this.context = context;
this.helper = helper;
}
@Override
public List<SecurityInfo> getAllByEndpoint(String endPoint) {
ReadResultSecurityStore store = lwM2MCredentialsSecurityInfoValidator.createAndValidateCredentialsSecurityInfo(endPoint, LwM2mTransportHandler.LwM2mTypeServer.BOOTSTRAP);
ReadResultSecurityStore store = lwM2MCredentialsSecurityInfoValidator.createAndValidateCredentialsSecurityInfo(endPoint, LwM2mTransportHandlerUtil.LwM2mTypeServer.BOOTSTRAP);
if (store.getBootstrapJsonCredential() != null && store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) {
/** add value to store from BootstrapJson */
this.setBootstrapConfigScurityInfo(store);
@ -93,7 +96,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
@Override
public SecurityInfo getByIdentity(String identity) {
ReadResultSecurityStore store = lwM2MCredentialsSecurityInfoValidator.createAndValidateCredentialsSecurityInfo(identity, LwM2mTransportHandler.LwM2mTypeServer.BOOTSTRAP);
ReadResultSecurityStore store = lwM2MCredentialsSecurityInfoValidator.createAndValidateCredentialsSecurityInfo(identity, LwM2mTransportHandlerUtil.LwM2mTypeServer.BOOTSTRAP);
if (store.getBootstrapJsonCredential() != null && store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) {
/** add value to store from BootstrapJson */
this.setBootstrapConfigScurityInfo(store);
@ -158,19 +161,19 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
LwM2MServerBootstrap profileServerBootstrap = mapper.readValue(bootstrapObject.get(BOOTSTRAP_SERVER).toString(), LwM2MServerBootstrap.class);
LwM2MServerBootstrap profileLwm2mServer = mapper.readValue(bootstrapObject.get(LWM2M_SERVER).toString(), LwM2MServerBootstrap.class);
UUID sessionUUiD = UUID.randomUUID();
TransportProtos.SessionInfoProto sessionInfo = context.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits());
TransportProtos.SessionInfoProto sessionInfo = helper.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits());
context.getTransportService().registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(null, sessionInfo));
if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) {
lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap);
lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer);
String logMsg = String.format("%s: getParametersBootstrap: %s Access connect client with bootstrap server.", LOG_LW2M_INFO, store.getEndPoint());
context.sendParametersOnThingsboardTelemetry(context.getKvLogyToThingsboard(logMsg), sessionInfo);
helper.sendParametersOnThingsboardTelemetry(helper.getKvLogyToThingsboard(logMsg), sessionInfo);
return lwM2MBootstrapConfig;
} else {
log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndPoint());
log.error("{} getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", LOG_LW2M_ERROR, store.getEndPoint());
String logMsg = String.format("%s: getParametersBootstrap: %s Different values SecurityMode between of client and profile.", LOG_LW2M_ERROR, store.getEndPoint());
context.sendParametersOnThingsboardTelemetry(context.getKvLogyToThingsboard(logMsg), sessionInfo);
helper.sendParametersOnThingsboardTelemetry(helper.getKvLogyToThingsboard(logMsg), sessionInfo);
return null;
}
}

27
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/client/LwM2mSoftwareUpdate.java

@ -0,0 +1,27 @@
/**
* Copyright © 2016-2021 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.transport.lwm2m.client;
import lombok.Data;
import java.util.UUID;
@Data
public class LwM2mSoftwareUpdate {
private volatile String clientSwVersion;
private volatile String currentSwVersion;
private volatile UUID currentSwId;
}

38
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MSecureServerConfig.java

@ -0,0 +1,38 @@
/**
* Copyright © 2016-2021 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.transport.lwm2m.config;
public interface LwM2MSecureServerConfig {
Integer getId();
String getHost();
Integer getPort();
String getSecureHost();
Integer getSecurePort();
String getPublicX();
String getPublicY();
String getPrivateEncoded();
String getCertificateAlias();
}

47
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigBootstrap.java → common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportBootstrapConfig.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.transport.lwm2m;
package org.thingsboard.server.transport.lwm2m.config;
import lombok.Getter;
import lombok.Setter;
@ -29,53 +29,42 @@ import java.util.Map;
@Slf4j
@Component
@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true') || '${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'")
public class LwM2MTransportConfigBootstrap {
@Getter
@Value("${transport.lwm2m.bootstrap.enable:}")
private Boolean bootstrapEnable;
public class LwM2MTransportBootstrapConfig implements LwM2MSecureServerConfig {
@Getter
@Value("${transport.lwm2m.bootstrap.id:}")
private Integer bootstrapServerId;
private Integer id;
@Getter
@Value("${transport.lwm2m.bootstrap.bind_address:}")
private String bootstrapHost;
private String host;
@Getter
@Value("${transport.lwm2m.bootstrap.bind_port_no_sec:}")
private Integer bootstrapPortNoSec;
@Value("${transport.lwm2m.bootstrap.bind_port:}")
private Integer port;
@Getter
@Value("${transport.lwm2m.bootstrap.secure.bind_address_security:}")
private String bootstrapHostSecurity;
@Value("${transport.lwm2m.bootstrap.security.bind_address:}")
private String secureHost;
@Getter
@Value("${transport.lwm2m.bootstrap.secure.bind_port_security:}")
private Integer bootstrapPortSecurity;
@Value("${transport.lwm2m.bootstrap.security.bind_port:}")
private Integer securePort;
@Getter
@Value("${transport.lwm2m.bootstrap.secure.public_x:}")
private String bootstrapPublicX;
@Value("${transport.lwm2m.bootstrap.security.public_x:}")
private String publicX;
@Getter
@Value("${transport.lwm2m.bootstrap.secure.public_y:}")
private String bootstrapPublicY;
@Value("${transport.lwm2m.bootstrap.security.public_y:}")
private String publicY;
@Getter
@Setter
private PublicKey bootstrapPublicKey;
@Value("${transport.lwm2m.bootstrap.security.private_encoded:}")
private String privateEncoded;
@Getter
@Value("${transport.lwm2m.bootstrap.secure.private_encoded:}")
private String bootstrapPrivateEncoded;
@Value("${transport.lwm2m.bootstrap.security.alias:}")
private String certificateAlias;
@Getter
@Value("${transport.lwm2m.bootstrap.secure.alias:}")
private String bootstrapAlias;
@Getter
@Setter
private Map<String /** clientEndPoint */, TransportProtos.ValidateDeviceCredentialsResponseMsg> sessions;
}

162
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java

@ -0,0 +1,162 @@
/**
* Copyright © 2016-2021 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.transport.lwm2m.config;
import com.google.common.io.Resources;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.server.model.LwM2mModelProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
@Slf4j
@Component
@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true') || '${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'")
public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig {
@Getter
@Setter
private LwM2mModelProvider modelProvider;
@Getter
@Value("${transport.lwm2m.timeout:}")
private Long timeout;
@Getter
@Value("${transport.sessions.report_timeout}")
private long sessionReportTimeout;
@Getter
@Value("${transport.lwm2m.recommended_ciphers:}")
private boolean recommendedCiphers;
@Getter
@Value("${transport.lwm2m.recommended_supported_groups:}")
private boolean recommendedSupportedGroups;
@Getter
@Value("${transport.lwm2m.response_pool_size:}")
private int responsePoolSize;
@Getter
@Value("${transport.lwm2m.registered_pool_size:}")
private int registeredPoolSize;
@Getter
@Value("${transport.lwm2m.update_registered_pool_size:}")
private int updateRegisteredPoolSize;
@Getter
@Value("${transport.lwm2m.un_registered_pool_size:}")
private int unRegisteredPoolSize;
@Getter
@Value("${transport.lwm2m.security.key_store_type:}")
private String keyStoreType;
@Getter
@Value("${transport.lwm2m.security.key_store:}")
private String keyStorePathFile;
@Getter
@Setter
private KeyStore keyStoreValue;
@Getter
@Value("${transport.lwm2m.security.key_store_password:}")
private String keyStorePassword;
@Getter
@Value("${transport.lwm2m.security.root_alias:}")
private String rootCertificateAlias;
@Getter
@Value("${transport.lwm2m.security.enable_gen_new_key_psk_rpk:}")
private Boolean enableGenNewKeyPskRpk;
@Getter
@Value("${transport.lwm2m.server.id:}")
private Integer id;
@Getter
@Value("${transport.lwm2m.server.bind_address:}")
private String host;
@Getter
@Value("${transport.lwm2m.server.bind_port:}")
private Integer port;
@Getter
@Value("${transport.lwm2m.server.security.bind_address:}")
private String secureHost;
@Getter
@Value("${transport.lwm2m.server.security.bind_port:}")
private Integer securePort;
@Getter
@Value("${transport.lwm2m.server.security.public_x:}")
private String publicX;
@Getter
@Value("${transport.lwm2m.server.security.public_y:}")
private String publicY;
@Getter
@Value("${transport.lwm2m.server.security.private_encoded:}")
private String privateEncoded;
@Getter
@Value("${transport.lwm2m.server.security.alias:}")
private String certificateAlias;
@Getter
@Value("${transport.lwm2m.log_max_length:}")
private int logMaxLength;
@PostConstruct
public void init() {
URI uri = null;
try {
uri = Resources.getResource(keyStorePathFile).toURI();
log.error("URI: {}", uri);
File keyStoreFile = new File(uri);
InputStream inKeyStore = new FileInputStream(keyStoreFile);
keyStoreValue = KeyStore.getInstance(keyStoreType);
keyStoreValue.load(inKeyStore, keyStorePassword == null ? null : keyStorePassword.toCharArray());
} catch (Exception e) {
log.warn("Unable to lookup LwM2M keystore. Reason: {}, {}" , uri, e.getMessage());
// Absence of the key store should not block user from using plain LwM2M
// throw new RuntimeException("Failed to lookup LwM2M keystore: " + (uri != null ? uri.toString() : ""), e);
}
}
}

29
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java

@ -16,6 +16,7 @@
package org.thingsboard.server.transport.lwm2m.secure;
import com.google.gson.JsonObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.util.Hex;
import org.eclipse.leshan.core.util.SecurityUtil;
@ -26,8 +27,10 @@ import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContextServer;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil;
import java.io.IOException;
import java.security.GeneralSecurityException;
@ -44,13 +47,11 @@ import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X5
@Slf4j
@Component
@TbLwM2mTransportComponent
@RequiredArgsConstructor
public class LwM2mCredentialsSecurityInfoValidator {
private final LwM2mTransportContextServer contextS;
public LwM2mCredentialsSecurityInfoValidator(LwM2mTransportContextServer contextS) {
this.contextS = contextS;
}
private final LwM2mTransportContext context;
private final LwM2MTransportServerConfig config;
/**
* Request to thingsboard Response from thingsboard ValidateDeviceLwM2MCredentials
@ -58,17 +59,17 @@ public class LwM2mCredentialsSecurityInfoValidator {
* @param keyValue -
* @return ValidateDeviceCredentialsResponseMsg and SecurityInfo
*/
public ReadResultSecurityStore createAndValidateCredentialsSecurityInfo(String endpoint, LwM2mTransportHandler.LwM2mTypeServer keyValue) {
public ReadResultSecurityStore createAndValidateCredentialsSecurityInfo(String endpoint, LwM2mTransportHandlerUtil.LwM2mTypeServer keyValue) {
CountDownLatch latch = new CountDownLatch(1);
final ReadResultSecurityStore[] resultSecurityStore = new ReadResultSecurityStore[1];
contextS.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(endpoint).build(),
context.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(endpoint).build(),
new TransportServiceCallback<>() {
@Override
public void onSuccess(ValidateDeviceCredentialsResponseMsg msg) {
String credentialsBody = msg.getCredentialsBody();
resultSecurityStore[0] = createSecurityInfo(endpoint, credentialsBody, keyValue);
resultSecurityStore[0].setMsg(msg);
Optional<DeviceProfile> deviceProfileOpt = LwM2mTransportHandler.decode(msg.getProfileBody().toByteArray());
Optional<DeviceProfile> deviceProfileOpt = LwM2mTransportHandlerUtil.decode(msg.getProfileBody().toByteArray());
deviceProfileOpt.ifPresent(profile -> resultSecurityStore[0].setDeviceProfile(profile));
latch.countDown();
}
@ -81,7 +82,7 @@ public class LwM2mCredentialsSecurityInfoValidator {
}
});
try {
latch.await(contextS.getLwM2MTransportConfigServer().getTimeout(), TimeUnit.MILLISECONDS);
latch.await(config.getTimeout(), TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
log.error("Failed to await credentials!", e);
}
@ -95,9 +96,9 @@ public class LwM2mCredentialsSecurityInfoValidator {
* @param keyValue -
* @return SecurityInfo
*/
private ReadResultSecurityStore createSecurityInfo(String endPoint, String jsonStr, LwM2mTransportHandler.LwM2mTypeServer keyValue) {
private ReadResultSecurityStore createSecurityInfo(String endPoint, String jsonStr, LwM2mTransportHandlerUtil.LwM2mTypeServer keyValue) {
ReadResultSecurityStore result = new ReadResultSecurityStore();
JsonObject objectMsg = LwM2mTransportHandler.validateJson(jsonStr);
JsonObject objectMsg = LwM2mTransportHandlerUtil.validateJson(jsonStr);
if (objectMsg != null && !objectMsg.isJsonNull()) {
JsonObject object = (objectMsg.has(keyValue.type) && !objectMsg.get(keyValue.type).isJsonNull()) ? objectMsg.get(keyValue.type).getAsJsonObject() : null;
/**
@ -108,7 +109,7 @@ public class LwM2mCredentialsSecurityInfoValidator {
&& objectMsg.get("client").getAsJsonObject().get("endpoint").isJsonPrimitive()) ? objectMsg.get("client").getAsJsonObject().get("endpoint").getAsString() : null;
endPoint = (endPointPsk == null || endPointPsk.isEmpty()) ? endPoint : endPointPsk;
if (object != null && !object.isJsonNull()) {
if (keyValue.equals(LwM2mTransportHandler.LwM2mTypeServer.BOOTSTRAP)) {
if (keyValue.equals(LwM2mTransportHandlerUtil.LwM2mTypeServer.BOOTSTRAP)) {
result.setBootstrapJsonCredential(object);
result.setEndPoint(endPoint);
result.setSecurityMode(LwM2MSecurityMode.fromSecurityMode(object.get("bootstrapServer").getAsJsonObject().get("securityMode").getAsString().toLowerCase()).code);

395
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java → common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java

@ -38,10 +38,15 @@ import org.eclipse.leshan.server.registration.Registration;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.cache.firmware.FirmwareDataCache;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.firmware.FirmwareKey;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.firmware.FirmwareUtil;
import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.common.transport.service.DefaultTransportService;
import org.thingsboard.server.gen.transport.TransportProtos;
@ -49,6 +54,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotif
import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile;
@ -77,34 +83,37 @@ import java.util.stream.Collectors;
import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST;
import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION;
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY;
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.CLIENT_NOT_AUTHORIZED;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEVICE_ATTRIBUTES_REQUEST;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_VALUE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LWM2M_STRATEGY_2;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.DISCOVER;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.EXECUTE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.OBSERVE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.OBSERVE_CANCEL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.OBSERVE_READ_ALL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.READ;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.WRITE_ATTRIBUTES;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.WRITE_REPLACE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.WRITE_UPDATE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.SERVICE_CHANNEL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertJsonArrayToSet;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromIdVerToObjectId;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromObjectIdToIdVer;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getAckCallback;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.validateObjectVerFromKey;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.CLIENT_NOT_AUTHORIZED;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.DEVICE_ATTRIBUTES_REQUEST;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.FR_OBJECT_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.FR_PATH_RESOURCE_VER_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_ERROR;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_INFO;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_VALUE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LWM2M_STRATEGY_2;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.DISCOVER;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.EXECUTE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.OBSERVE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.OBSERVE_CANCEL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.OBSERVE_READ_ALL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.READ;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.WRITE_ATTRIBUTES;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.WRITE_REPLACE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.WRITE_UPDATE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.SERVICE_CHANNEL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.convertJsonArrayToSet;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.convertPathFromIdVerToObjectId;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.convertPathFromObjectIdToIdVer;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.getAckCallback;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.validateObjectVerFromKey;
@Slf4j
@Service
@TbLwM2mTransportComponent
public class LwM2mTransportServiceImpl implements LwM2mTransportService {
public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler {
private ExecutorService executorRegistered;
private ExecutorService executorUpdateRegistered;
@ -112,31 +121,35 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
private LwM2mValueConverterImpl converter;
private final TransportService transportService;
public final LwM2mTransportContextServer lwM2mTransportContextServer;
private final LwM2mTransportContext context;
private final LwM2MTransportServerConfig config;
private final FirmwareDataCache firmwareDataCache;
private final LwM2mTransportServerHelper helper;
private final LwM2mClientContext lwM2mClientContext;
private final LeshanServer leshanServer;
private final LwM2mTransportRequest lwM2mTransportRequest;
public LwM2mTransportServiceImpl(TransportService transportService, LwM2mTransportContextServer lwM2mTransportContextServer, LwM2mClientContext lwM2mClientContext, LeshanServer leshanServer, @Lazy LwM2mTransportRequest lwM2mTransportRequest) {
public DefaultLwM2MTransportMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper,
LwM2mClientContext lwM2mClientContext,
@Lazy LwM2mTransportRequest lwM2mTransportRequest,
FirmwareDataCache firmwareDataCache,
LwM2mTransportContext context) {
this.transportService = transportService;
this.lwM2mTransportContextServer = lwM2mTransportContextServer;
this.config = config;
this.helper = helper;
this.lwM2mClientContext = lwM2mClientContext;
this.leshanServer = leshanServer;
this.lwM2mTransportRequest = lwM2mTransportRequest;
this.firmwareDataCache = firmwareDataCache;
this.context = context;
}
@PostConstruct
public void init() {
this.lwM2mTransportContextServer.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) lwM2mTransportContextServer.getLwM2MTransportConfigServer().getSessionReportTimeout()), lwM2mTransportContextServer.getLwM2MTransportConfigServer().getSessionReportTimeout(), TimeUnit.MILLISECONDS);
this.executorRegistered = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getRegisteredPoolSize(),
this.context.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) config.getSessionReportTimeout()), config.getSessionReportTimeout(), TimeUnit.MILLISECONDS);
this.executorRegistered = Executors.newFixedThreadPool(this.config.getRegisteredPoolSize(),
new NamedThreadFactory(String.format("LwM2M %s channel registered", SERVICE_CHANNEL)));
this.executorUpdateRegistered = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getUpdateRegisteredPoolSize(),
this.executorUpdateRegistered = Executors.newFixedThreadPool(this.config.getUpdateRegisteredPoolSize(),
new NamedThreadFactory(String.format("LwM2M %s channel update registered", SERVICE_CHANNEL)));
this.executorUnRegistered = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getUnRegisteredPoolSize(),
this.executorUnRegistered = Executors.newFixedThreadPool(this.config.getUnRegisteredPoolSize(),
new NamedThreadFactory(String.format("LwM2M %s channel un registered", SERVICE_CHANNEL)));
this.converter = LwM2mValueConverterImpl.getInstance();
}
@ -168,8 +181,9 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null);
transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null);
transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().build(), null);
this.getInfoFirmwareUpdate(lwM2MClient);
this.initLwM2mFromClientValue(registration, lwM2MClient);
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration);
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration.getId());
} else {
log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
}
@ -224,7 +238,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
executorUnRegistered.submit(() -> {
try {
this.setCancelObservations(registration);
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration);
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId());
this.closeClientSession(registration);
} catch (Throwable t) {
log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t);
@ -257,7 +271,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
@Override
public void onSleepingDev(Registration registration) {
log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint());
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is sleeping!", registration);
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is sleeping!", registration.getId());
//TODO: associate endpointId with device information.
}
@ -265,10 +279,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
@Override
public void setCancelObservations(Registration registration) {
if (registration != null) {
Set<Observation> observations = leshanServer.getObservationService().getObservations(registration);
Set<Observation> observations = context.getServer().getObservationService().getObservations(registration);
observations.forEach(observation -> lwM2mTransportRequest.sendAllRequest(registration,
convertPathFromObjectIdToIdVer(observation.getPath().toString(), registration), OBSERVE_CANCEL,
null, null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null));
null, null, this.config.getTimeout(), null));
}
}
@ -280,7 +294,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
* @param response - observe
*/
@Override
public void onObservationResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest) {
public void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest) {
if (response.getContent() != null) {
if (response.getContent() instanceof LwM2mObject) {
LwM2mObject lwM2mObject = (LwM2mObject) response.getContent();
@ -304,22 +318,29 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
/**
* Update - send request in change value resources in Client
* Path to resources from profile equal keyName or from ModelObject equal name
* Only for resources: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase)
* Delete - nothing *
* 1. FirmwareUpdate:
* - If msg.getSharedUpdatedList().forEach(tsKvProto -> {tsKvProto.getKv().getKey().indexOf(FIRMWARE_UPDATE_PREFIX, 0) == 0
* 2. Shared Other AttributeUpdate
* -- Path to resources from profile equal keyName or from ModelObject equal name
* -- Only for resources: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase)
* 3. Delete - nothing
*
* @param msg -
*/
@Override
public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {
LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
if (msg.getSharedUpdatedCount() > 0) {
msg.getSharedUpdatedList().forEach(tsKvProto -> {
String pathName = tsKvProto.getKv().getKey();
String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName);
Object valueNew = this.lwM2mTransportContextServer.getValueFromKvProto(tsKvProto.getKv());
LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
Object valueNew = this.helper.getValueFromKvProto(tsKvProto.getKv());
//TODO: react on change of the firmware name.
if (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFrUpdate().getCurrentFwVersion())) {
this.getInfoFirmwareUpdate(lwM2MClient);
}
if (pathIdVer != null) {
ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer()
ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.config
.getModelProvider());
if (resourceModel != null && resourceModel.operations.isWritable()) {
this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), valueNew, pathIdVer);
@ -327,19 +348,26 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", pathIdVer, valueNew);
String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated",
LOG_LW2M_ERROR, pathIdVer, valueNew);
this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId());
}
} else {
log.error("Resource name name - [{}] value - [{}] is not present as attribute/telemetry in profile and cannot be updated", pathName, valueNew);
String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated",
LOG_LW2M_ERROR, pathName, valueNew);
this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId());
}
});
} else if (msg.getSharedDeletedCount() > 0) {
msg.getSharedUpdatedList().forEach(tsKvProto -> {
String pathName = tsKvProto.getKv().getKey();
Object valueNew = this.helper.getValueFromKvProto(tsKvProto.getKv());
if (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFrUpdate().getCurrentFwVersion())) {
lwM2MClient.getFrUpdate().setCurrentFwVersion((String) valueNew);
}
});
log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo);
}
}
/**
@ -377,7 +405,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
@Override
public void onResourceUpdate(Optional<TransportProtos.ResourceUpdateMsg> resourceUpdateMsgOpt) {
String idVer = resourceUpdateMsgOpt.get().getResourceKey();
lwM2mClientContext.getLwM2mClients().values().stream().forEach(e -> e.updateResourceModel(idVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getModelProvider()));
lwM2mClientContext.getLwM2mClients().values().stream().forEach(e -> e.updateResourceModel(idVer, this.config.getModelProvider()));
}
/**
@ -386,7 +414,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
@Override
public void onResourceDelete(Optional<TransportProtos.ResourceDeleteMsg> resourceDeleteMsgOpt) {
String pathIdVer = resourceDeleteMsgOpt.get().getResourceKey();
lwM2mClientContext.getLwM2mClients().values().stream().forEach(e -> e.deleteResources(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getModelProvider()));
lwM2mClientContext.getLwM2mClients().values().stream().forEach(e -> e.deleteResources(pathIdVer, this.config.getModelProvider()));
}
@Override
@ -402,7 +430,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
} else {
lwM2mTransportRequest.sendAllRequest(registration, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(), lwm2mClientRpcRequest.getContentFormatName(),
lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(),
this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), lwm2mClientRpcRequest);
this.config.getTimeout(), lwm2mClientRpcRequest);
}
} catch (Exception e) {
if (lwm2mClientRpcRequest == null) {
@ -430,7 +458,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
lwm2mClientRpcRequest.setRequestId(toDeviceRequest.getRequestId());
lwm2mClientRpcRequest.setSessionInfo(sessionInfo);
lwm2mClientRpcRequest.setValidTypeOper(toDeviceRequest.getMethodName());
JsonObject rpcRequest = LwM2mTransportHandler.validateJson(toDeviceRequest.getParams());
JsonObject rpcRequest = LwM2mTransportHandlerUtil.validateJson(toDeviceRequest.getParams());
if (rpcRequest != null) {
if (rpcRequest.has(lwm2mClientRpcRequest.keyNameKey)) {
String targetIdVer = this.getPresentPathIntoProfile(sessionInfo,
@ -455,23 +483,21 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
}
if (rpcRequest.has(lwm2mClientRpcRequest.paramsKey) && rpcRequest.get(lwm2mClientRpcRequest.paramsKey).isJsonObject()) {
lwm2mClientRpcRequest.setParams(new Gson().fromJson(rpcRequest.get(lwm2mClientRpcRequest.paramsKey)
.getAsJsonObject().toString(), new TypeToken<ConcurrentHashMap<String, Object>>() {
}.getType()));
.getAsJsonObject().toString(), new TypeToken<ConcurrentHashMap<String, Object>>() {
}.getType()));
}
lwm2mClientRpcRequest.setSessionInfo(sessionInfo);
if (OBSERVE_READ_ALL != lwm2mClientRpcRequest.getTypeOper() && lwm2mClientRpcRequest.getTargetIdVer() == null) {
lwm2mClientRpcRequest.setErrorMsg(lwm2mClientRpcRequest.targetIdVerKey + " and " +
lwm2mClientRpcRequest.keyNameKey + " is null or bad format");
}
else if ((EXECUTE == lwm2mClientRpcRequest.getTypeOper()
} else if ((EXECUTE == lwm2mClientRpcRequest.getTypeOper()
|| WRITE_REPLACE == lwm2mClientRpcRequest.getTypeOper())
&& lwm2mClientRpcRequest.getTargetIdVer() !=null
&& lwm2mClientRpcRequest.getTargetIdVer() != null
&& !(new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResource()
|| new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResourceInstance())) {
lwm2mClientRpcRequest.setErrorMsg("Invalid parameter " + lwm2mClientRpcRequest.targetIdVerKey
lwm2mClientRpcRequest.setErrorMsg("Invalid parameter " + lwm2mClientRpcRequest.targetIdVerKey
+ ". Only Resource or ResourceInstance can be this operation");
}
else if (WRITE_UPDATE == lwm2mClientRpcRequest.getTypeOper()){
} else if (WRITE_UPDATE == lwm2mClientRpcRequest.getTypeOper()) {
lwm2mClientRpcRequest.setErrorMsg("Procedures In Development...");
}
} else {
@ -483,23 +509,23 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
return lwm2mClientRpcRequest;
}
public void sentRpcRequest (Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) {
public void sentRpcRequest(Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) {
rpcRequest.setResponseCode(requestCode);
if (LOG_LW2M_ERROR.equals(typeMsg)) {
rpcRequest.setInfoMsg(null);
rpcRequest.setValueMsg(null);
if (rpcRequest.getErrorMsg() == null) {
msg = msg.isEmpty() ? null : msg;
rpcRequest.setErrorMsg(msg);
}
} else if (LOG_LW2M_INFO.equals(typeMsg)) {
if (rpcRequest.getInfoMsg() == null) {
rpcRequest.setInfoMsg(msg);
}
} else if (LOG_LW2M_VALUE.equals(typeMsg)) {
if (rpcRequest.getValueMsg() == null) {
rpcRequest.setValueMsg(msg);
}
if (LOG_LW2M_ERROR.equals(typeMsg)) {
rpcRequest.setInfoMsg(null);
rpcRequest.setValueMsg(null);
if (rpcRequest.getErrorMsg() == null) {
msg = msg.isEmpty() ? null : msg;
rpcRequest.setErrorMsg(msg);
}
} else if (LOG_LW2M_INFO.equals(typeMsg)) {
if (rpcRequest.getInfoMsg() == null) {
rpcRequest.setInfoMsg(msg);
}
} else if (LOG_LW2M_VALUE.equals(typeMsg)) {
if (rpcRequest.getValueMsg() == null) {
rpcRequest.setValueMsg(msg);
}
}
this.onToDeviceRpcResponse(rpcRequest.getDeviceRpcResponseResultMsg(), rpcRequest.getSessionInfo());
}
@ -521,7 +547,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
@Override
public void doTrigger(Registration registration, String path) {
lwM2mTransportRequest.sendAllRequest(registration, path, EXECUTE,
ContentFormat.TLV.getName(), null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null);
ContentFormat.TLV.getName(), null, this.config.getTimeout(), null);
}
/**
@ -554,9 +580,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
*
* @param registration -
*/
protected void onAwakeDev(Registration registration) {
@Override
public void onAwakeDev(Registration registration) {
log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint());
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is awake!", registration);
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is awake!", registration.getId());
//TODO: associate endpointId with device information.
}
@ -583,12 +610,16 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
/**
* @param logMsg - text msg
* @param registration - Id of Registration LwM2M Client
* @param registrationId - Id of Registration LwM2M Client
*/
public void sendLogsToThingsboard(String logMsg, Registration registration) {
SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
@Override
public void sendLogsToThingsboard(String logMsg, String registrationId) {
SessionInfoProto sessionInfo = this.getValidateSessionInfo(registrationId);
if (logMsg != null && sessionInfo != null) {
this.lwM2mTransportContextServer.sendParametersOnThingsboardTelemetry(this.lwM2mTransportContextServer.getKvLogyToThingsboard(logMsg), sessionInfo);
if(logMsg.length() > 1024){
logMsg = logMsg.substring(0, 1024);
}
this.helper.sendParametersOnThingsboardTelemetry(this.helper.getKvLogyToThingsboard(logMsg), sessionInfo);
}
}
@ -608,11 +639,11 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
LwM2mClientProfile lwM2MClientProfile = lwM2mClientContext.getProfile(registration);
Set<String> clientObjects = lwM2mClientContext.getSupportedIdVerInClient(registration);
if (clientObjects != null && clientObjects.size() > 0) {
if (LWM2M_STRATEGY_2 == LwM2mTransportHandler.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) {
if (LWM2M_STRATEGY_2 == LwM2mTransportHandlerUtil.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) {
// #2
lwM2MClient.getPendingRequests().addAll(clientObjects);
lwM2MClient.getPendingReadRequests().addAll(clientObjects);
clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, READ, ContentFormat.TLV.getName(),
null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null));
null, this.config.getTimeout(), null));
}
// #1
this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, READ, clientObjects);
@ -652,6 +683,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
* Sending observe value of resources to thingsboard
* #1 Return old Value Resource from LwM2MClient
* #2 Update new Resources (replace old Resource Value on new Resource Value)
* #3 If fr_update -> UpdateFirmware
* #4 updateAttrTelemetry
*
* @param registration - Registration LwM2M Client
* @param lwM2mResource - LwM2mSingleResource response.getContent()
@ -659,8 +692,21 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
*/
private void updateResourcesValue(Registration registration, LwM2mResource lwM2mResource, String path) {
LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null);
if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer()
if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.config
.getModelProvider())) {
if (FR_PATH_RESOURCE_VER_ID.equals(convertPathFromIdVerToObjectId(path)) &&
lwM2MClient.getFrUpdate().getCurrentFwVersion() != null
&& !lwM2MClient.getFrUpdate().getCurrentFwVersion().equals(lwM2MClient.getFrUpdate().getClientFwVersion())
&& lwM2MClient.isUpdateFw()) {
/** version != null
* set setClient_fw_version = value
**/
lwM2MClient.setUpdateFw(false);
lwM2MClient.getFrUpdate().setClientFwVersion(lwM2mResource.getValue().toString());
log.warn("updateFirmwareClient3");
this.updateFirmwareClient(lwM2MClient);
}
Set<String> paths = new HashSet<>();
paths.add(path);
this.updateAttrTelemetry(registration, paths);
@ -669,6 +715,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
}
}
/**
* send Attribute and Telemetry to Thingsboard
* #1 - get AttrName/TelemetryName with value from LwM2MClient:
@ -684,10 +731,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
if (results != null && sessionInfo != null) {
if (results.getResultAttributes().size() > 0) {
this.lwM2mTransportContextServer.sendParametersOnThingsboardAttribute(results.getResultAttributes(), sessionInfo);
this.helper.sendParametersOnThingsboardAttribute(results.getResultAttributes(), sessionInfo);
}
if (results.getResultTelemetries().size() > 0) {
this.lwM2mTransportContextServer.sendParametersOnThingsboardTelemetry(results.getResultTelemetries(), sessionInfo);
this.helper.sendParametersOnThingsboardTelemetry(results.getResultTelemetries(), sessionInfo);
}
}
} catch (Exception e) {
@ -723,22 +770,23 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile());
result = params.keySet();
}
if (!result.isEmpty()) {
if (result != null && !result.isEmpty()) {
// #1
Set<String> pathSend = result.stream().filter(target -> {
return target.split(LWM2M_SEPARATOR_PATH).length < 3 ?
clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]) :
clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1] + "/" + target.split(LWM2M_SEPARATOR_PATH)[2]);
}
)
.collect(Collectors.toUnmodifiableSet());
).collect(Collectors.toUnmodifiableSet());
if (!pathSend.isEmpty()) {
lwM2MClient.getPendingRequests().addAll(pathSend);
lwM2MClient.getPendingReadRequests().addAll(pathSend);
ConcurrentHashMap<String, Object> finalParams = params;
pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(),
finalParams != null ? finalParams.get(target) : null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null));
pathSend.forEach(target -> {
lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(),
finalParams != null ? finalParams.get(target) : null, this.config.getTimeout(), null);
});
if (OBSERVE.equals(typeOper)) {
lwM2MClient.initValue(this, null);
lwM2MClient.initReadValue(this, null);
}
}
}
@ -820,7 +868,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
LwM2mResource resourceValue = lwM2MClient != null ? getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) : null;
if (resourceValue != null) {
ResourceModel.Type currentType = resourceValue.getType();
ResourceModel.Type expectedType = this.lwM2mTransportContextServer.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer);
ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer);
Object valueKvProto = null;
if (resourceValue.isMultiInstances()) {
valueKvProto = new JsonObject();
@ -837,7 +885,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
valueKvProto = this.converter.convertValue(resourceValue.getValue(), currentType, expectedType,
new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)));
}
return valueKvProto != null ? this.lwM2mTransportContextServer.getKvAttrTelemetryToThingsboard(currentType, resourceName, valueKvProto, resourceValue.isMultiInstances()) : null;
return valueKvProto != null ? this.helper.getKvAttrTelemetryToThingsboard(currentType, resourceName, valueKvProto, resourceValue.isMultiInstances()) : null;
}
} catch (Exception e) {
log.error("Failed to add parameters.", e);
@ -856,7 +904,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
private Object getResourceValueFormatKv(LwM2mClient lwM2MClient, String pathIdVer) {
LwM2mResource resourceValue = this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer);
ResourceModel.Type currentType = resourceValue.getType();
ResourceModel.Type expectedType = this.lwM2mTransportContextServer.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer);
ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer);
return this.converter.convertValue(resourceValue.getValue(), currentType, expectedType,
new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)));
}
@ -969,7 +1017,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
// update value in Resources
registrationIds.forEach(registrationId -> {
Registration registration = lwM2mClientContext.getRegistration(registrationId);
this.readResourceValueObserve(registration, sendAttrToThingsboard.getPathPostParametersAdd(), READ);
this.readObserveFromProfile(registration, sendAttrToThingsboard.getPathPostParametersAdd(), READ);
// send attr/telemetry to tingsboard for new path
this.updateAttrTelemetry(registration, sendAttrToThingsboard.getPathPostParametersAdd());
});
@ -998,12 +1046,12 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
registrationIds.forEach(registrationId -> {
Registration registration = lwM2mClientContext.getRegistration(registrationId);
if (postObserveAnalyzer.getPathPostParametersAdd().size() > 0) {
this.readResourceValueObserve(registration, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE);
this.readObserveFromProfile(registration, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE);
}
// 5.3 del
// send Request cancel observe to Client
if (postObserveAnalyzer.getPathPostParametersDel().size() > 0) {
this.cancelObserveIsValue(registration, postObserveAnalyzer.getPathPostParametersDel());
this.cancelObserveFromProfile(registration, postObserveAnalyzer.getPathPostParametersDel());
}
});
}
@ -1043,16 +1091,16 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
* @param registration - Registration LwM2M Client
* @param targets - path Resources == [ "/2/0/0", "/2/0/1"]
*/
private void readResourceValueObserve(Registration registration, Set<String> targets, LwM2mTypeOper typeOper) {
private void readObserveFromProfile(Registration registration, Set<String> targets, LwM2mTypeOper typeOper) {
targets.forEach(target -> {
LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(target));
if (pathIds.isResource()) {
if (READ.equals(typeOper)) {
lwM2mTransportRequest.sendAllRequest(registration, target, typeOper,
ContentFormat.TLV.getName(), null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null);
ContentFormat.TLV.getName(), null, this.config.getTimeout(), null);
} else if (OBSERVE.equals(typeOper)) {
lwM2mTransportRequest.sendAllRequest(registration, target, typeOper,
null, null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null);
null, null, this.config.getTimeout(), null);
}
}
});
@ -1108,7 +1156,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
if (!pathSend.isEmpty()) {
ConcurrentHashMap<String, Object> finalParams = lwm2mAttributesNew;
pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(registration, target, WRITE_ATTRIBUTES, ContentFormat.TLV.getName(),
finalParams.get(target), this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null));
finalParams.get(target), this.config.getTimeout(), null));
}
});
}
@ -1125,7 +1173,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
params.clear();
params.put(OBJECT_VERSION, "");
lwM2mTransportRequest.sendAllRequest(registration, target, WRITE_ATTRIBUTES, ContentFormat.TLV.getName(),
params, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null);
params, this.config.getTimeout(), null);
});
}
});
@ -1133,12 +1181,12 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
}
private void cancelObserveIsValue(Registration registration, Set<String> paramAnallyzer) {
private void cancelObserveFromProfile(Registration registration, Set<String> paramAnallyzer) {
LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null);
paramAnallyzer.forEach(pathIdVer -> {
if (this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) != null) {
lwM2mTransportRequest.sendAllRequest(registration, pathIdVer, OBSERVE_CANCEL, null,
null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null);
null, this.config.getTimeout(), null);
}
}
);
@ -1148,12 +1196,12 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
if (valueNew != null && (valueOld == null || !valueNew.toString().equals(valueOld.toString()))) {
lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), path, WRITE_REPLACE,
ContentFormat.TLV.getName(), valueNew,
this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null);
this.config.getTimeout(), null);
} else {
log.error("Failed update resource [{}] [{}]", path, valueNew);
String logMsg = String.format("%s: Failed update resource path - %s value - %s. Value is not changed or bad",
LOG_LW2M_ERROR, path, valueNew);
this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId());
log.info("Failed update resource [{}] [{}]", path, valueNew);
}
}
@ -1182,8 +1230,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
}
/**
* Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values
* #1 Get path resource by result attributesResponse
* 1. FirmwareUpdate:
* - msg.getSharedUpdatedList().forEach(tsKvProto -> {tsKvProto.getKv().getKey().indexOf(FIRMWARE_UPDATE_PREFIX, 0) == 0
* 2. Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values
* - Get path resource by result attributesResponse
*
* @param attributesResponse -
* @param sessionInfo -
@ -1191,6 +1241,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) {
try {
List<TransportProtos.TsKvProto> tsKvProtos = attributesResponse.getSharedAttributeListList();
this.updateAttriuteFromThingsboard(tsKvProtos, sessionInfo);
} catch (Exception e) {
log.error(String.valueOf(e));
@ -1223,7 +1274,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
// #2.1
lwM2MClient.getDelayedRequests().forEach((pathIdVer, tsKvProto) -> {
this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer),
this.lwM2mTransportContextServer.getValueFromKvProto(tsKvProto.getKv()), pathIdVer);
this.helper.getValueFromKvProto(tsKvProto.getKv()), pathIdVer);
});
}
@ -1240,7 +1291,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
return null;
} else {
return SessionInfoProto.newBuilder()
.setNodeId(this.lwM2mTransportContextServer.getNodeId())
.setNodeId(this.context.getNodeId())
.setSessionIdMSB(lwM2MClient.getSessionId().getMostSignificantBits())
.setSessionIdLSB(lwM2MClient.getSessionId().getLeastSignificantBits())
.setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB())
@ -1274,7 +1325,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
*/
private SessionInfoProto getValidateSessionInfo(String registrationId) {
LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(null, registrationId);
return getNewSessionInfoProto(lwM2MClient);
return lwM2MClient != null ? this.getNewSessionInfoProto(lwM2MClient) : null;
}
/**
@ -1293,28 +1344,88 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
}
/**
* !!! sharedAttr === profileAttr !!!
* If there is a difference in values between the current resource values and the shared attribute values
* when the client connects to the server
* #1 get attributes name from profile include name resources in ModelObject if resource isWritable
* #2.1 #1 size > 0 => send Request getAttributes to thingsboard
* #1. !!! sharedAttr === profileAttr !!!
* - If there is a difference in values between the current resource values and the shared attribute values
* - when the client connects to the server
* #1.1 get attributes name from profile include name resources in ModelObject if resource isWritable
* #1.2 #1 size > 0 => send Request getAttributes to thingsboard
* #2. FirmwareAttribute subscribe:
*
* @param lwM2MClient - LwM2M Client
*/
public void putDelayedUpdateResourcesThingsboard(LwM2mClient lwM2MClient) {
SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration());
if (sessionInfo != null) {
//#1.1 + #1.2
List<String> attrSharedNames = this.getNamesAttrFromProfileIsWritable(lwM2MClient);
if (attrSharedNames.size() > 0) {
//#2.1
//#1.1
ConcurrentMap<String, String> keyNamesMap = this.getNamesFromProfileForSharedAttributes(lwM2MClient);
if (keyNamesMap.values().size() > 0) {
try {
TransportProtos.GetAttributeRequestMsg getAttributeMsg = lwM2mTransportContextServer.getAdaptor().convertToGetAttributes(null, attrSharedNames);
//#1.2
TransportProtos.GetAttributeRequestMsg getAttributeMsg = helper.getAdaptor().convertToGetAttributes(null, keyNamesMap.values());
transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST));
} catch (AdaptorException e) {
log.warn("Failed to decode get attributes request", e);
}
}
}
}
public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient) {
SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration());
if (sessionInfo != null) {
TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
.setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
.setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
.setTenantIdMSB(sessionInfo.getTenantIdMSB())
.setTenantIdLSB(sessionInfo.getTenantIdLSB())
.setType(FirmwareType.FIRMWARE.name())
.build();
transportService.process(sessionInfo, getFirmwareRequestMsg,
new TransportServiceCallback<>() {
@Override
public void onSuccess(TransportProtos.GetFirmwareResponseMsg response) {
if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) {
lwM2MClient.getFrUpdate().setCurrentFwVersion(response.getVersion());
lwM2MClient.getFrUpdate().setCurrentFwId(new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())).getId());
lwM2MClient.setUpdateFw(true);
readRequestToClientFirmwareVer(lwM2MClient.getRegistration());
} else {
log.trace("Firmware [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString());
}
}
@Override
public void onError(Throwable e) {
log.trace("Failed to process credentials ", e);
}
});
}
}
/**
* @param registration
*/
public void readRequestToClientFirmwareVer(Registration registration) {
String pathIdVer = convertPathFromObjectIdToIdVer(FR_PATH_RESOURCE_VER_ID, registration);
lwM2mTransportRequest.sendAllRequest(registration, pathIdVer, READ, ContentFormat.TLV.getName(),
null, config.getTimeout(), null);
}
/**
*
* @param lwM2MClient -
*/
public void updateFirmwareClient(LwM2mClient lwM2MClient) {
if (!lwM2MClient.getFrUpdate().getCurrentFwVersion().equals(lwM2MClient.getFrUpdate().getClientFwVersion())) {
int chunkSize = 0;
int chunk = 0;
byte[] firmwareChunk = firmwareDataCache.get(lwM2MClient.getFrUpdate().getCurrentFwId().toString(), chunkSize, chunk);
String verSupportedObject = lwM2MClient.getRegistration().getSupportedObject().get(FR_OBJECT_ID);
String targetIdVer = LWM2M_SEPARATOR_PATH + FR_OBJECT_ID + LWM2M_SEPARATOR_KEY + verSupportedObject + LWM2M_SEPARATOR_PATH + 0 + LWM2M_SEPARATOR_PATH + 0;
lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE.getName(),
firmwareChunk, config.getTimeout(), null);
log.warn("updateFirmwareClient [{}] [{}]", lwM2MClient.getFrUpdate().getCurrentFwVersion(), lwM2MClient.getFrUpdate().getClientFwVersion());
}
}
@ -1326,27 +1437,16 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
* @param lwM2MClient -
* @return ArrayList keyNames from profile profileAttr && IsWritable
*/
private List<String> getNamesAttrFromProfileIsWritable(LwM2mClient lwM2MClient) {
private ConcurrentMap<String, String> getNamesFromProfileForSharedAttributes(LwM2mClient lwM2MClient) {
LwM2mClientProfile profile = lwM2mClientContext.getProfile(lwM2MClient.getProfileId());
Set<String> attrSet = new Gson().fromJson(profile.getPostAttributeProfile(),
new TypeToken<HashSet<String>>() {
}.getType());
ConcurrentMap<String, String> keyNamesMap = new Gson().fromJson(profile.getPostKeyNameProfile().toString(),
return new Gson().fromJson(profile.getPostKeyNameProfile().toString(),
new TypeToken<ConcurrentHashMap<String, String>>() {
}.getType());
ConcurrentMap<String, String> keyNamesIsWritable = keyNamesMap.entrySet()
.stream()
.filter(e -> (attrSet.contains(e.getKey()) && validateResourceInModel(lwM2MClient, e.getKey(), true)))
.collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue));
Set<String> namesIsWritable = ConcurrentHashMap.newKeySet();
namesIsWritable.addAll(new HashSet<>(keyNamesIsWritable.values()));
return new ArrayList<>(namesIsWritable);
}
private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) {
ResourceModel resourceModel = lwM2mClient.getResourceModel(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer()
ResourceModel resourceModel = lwM2mClient.getResourceModel(pathIdVer, this.config
.getModelProvider());
Integer objectId = new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)).getObjectId();
String objectVer = validateObjectVerFromKey(pathIdVer);
@ -1355,9 +1455,4 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId)));
}
@Override
public String getName() {
return "LWM2M";
}
}

160
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerConfiguration.java → common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java

@ -15,7 +15,10 @@
*/
package org.thingsboard.server.transport.lwm2m.server;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.network.config.NetworkConfig;
import org.eclipse.californium.core.network.stack.BlockwiseLayer;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder;
@ -27,12 +30,16 @@ import org.eclipse.leshan.server.model.LwM2mModelProvider;
import org.eclipse.leshan.server.security.DefaultAuthorizer;
import org.eclipse.leshan.server.security.EditableSecurityStore;
import org.eclipse.leshan.server.security.SecurityChecker;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.math.BigInteger;
import java.security.AlgorithmParameters;
import java.security.KeyFactory;
@ -57,61 +64,86 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8;
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256;
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getCoapConfig;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig;
@Slf4j
@Component
@TbLwM2mTransportComponent
public class LwM2mTransportServerConfiguration {
@RequiredArgsConstructor
public class DefaultLwM2mTransportService implements LwM2MTransportService {
private PublicKey publicKey;
private PrivateKey privateKey;
private boolean pskMode = false;
private final LwM2mTransportContextServer context;
private final LwM2mTransportContext context;
private final LwM2MTransportServerConfig config;
private final LwM2mTransportServerHelper helper;
private final LwM2mTransportMsgHandler handler;
private final CaliforniumRegistrationStore registrationStore;
private final EditableSecurityStore securityStore;
private final LwM2mClientContext lwM2mClientContext;
public LwM2mTransportServerConfiguration(LwM2mTransportContextServer context, CaliforniumRegistrationStore registrationStore, EditableSecurityStore securityStore, LwM2mClientContext lwM2mClientContext) {
this.context = context;
this.registrationStore = registrationStore;
this.securityStore = securityStore;
this.lwM2mClientContext = lwM2mClientContext;
private LeshanServer server;
@PostConstruct
public void init() {
if (config.getEnableGenNewKeyPskRpk()) {
new LWM2MGenerationPSkRPkECC();
}
this.server = getLhServer(config.getPort(), config.getSecurePort());
this.startLhServer();
this.context.setServer(server);
}
private void startLhServer() {
log.info("Starting LwM2M transport Server...");
this.server.start();
LwM2mServerListener lhServerCertListener = new LwM2mServerListener(handler);
this.server.getRegistrationService().addListener(lhServerCertListener.registrationListener);
this.server.getPresenceService().addListener(lhServerCertListener.presenceListener);
this.server.getObservationService().addListener(lhServerCertListener.observationListener);
}
@Bean
public LeshanServer getLeshanServer() {
log.info("Starting LwM2M transport Server... PostConstruct");
return this.getLhServer(this.context.getLwM2MTransportConfigServer().getServerPortNoSec(), this.context.getLwM2MTransportConfigServer().getServerPortSecurity());
@PreDestroy
public void shutdown() {
log.info("Stopping LwM2M transport Server!");
server.destroy();
log.info("LwM2M transport Server stopped!");
}
private LeshanServer getLhServer(Integer serverPortNoSec, Integer serverSecurePort) {
LeshanServerBuilder builder = new LeshanServerBuilder();
builder.setLocalAddress(this.context.getLwM2MTransportConfigServer().getServerHost(), serverPortNoSec);
builder.setLocalSecureAddress(this.context.getLwM2MTransportConfigServer().getServerHostSecurity(), serverSecurePort);
builder.setLocalAddress(config.getHost(), serverPortNoSec);
builder.setLocalSecureAddress(config.getSecureHost(), serverSecurePort);
builder.setDecoder(new DefaultLwM2mNodeDecoder());
/** Use a magic converter to support bad type send by the UI. */
/* Use a magic converter to support bad type send by the UI. */
builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance()));
/** Create CoAP Config */
/* Create CoAP Config */
NetworkConfig networkConfig = getCoapConfig(serverPortNoSec, serverSecurePort);
BlockwiseLayer blockwiseLayer = new BlockwiseLayer(networkConfig);
builder.setCoapConfig(getCoapConfig(serverPortNoSec, serverSecurePort));
/** Define model provider (Create Models )*/
LwM2mModelProvider modelProvider = new LwM2mVersionedModelProvider(this.lwM2mClientContext, this.context);
this.context.getLwM2MTransportConfigServer().setModelProvider(modelProvider);
/* Define model provider (Create Models )*/
LwM2mModelProvider modelProvider = new LwM2mVersionedModelProvider(this.lwM2mClientContext, this.helper, this.context);
config.setModelProvider(modelProvider);
builder.setObjectModelProvider(modelProvider);
/** Create credentials */
/* Create credentials */
this.setServerWithCredentials(builder);
/** Set securityStore with new registrationStore */
/* Set securityStore with new registrationStore */
builder.setSecurityStore(securityStore);
builder.setRegistrationStore(registrationStore);
/** Create DTLS Config */
/* Create DTLS Config */
DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder();
dtlsConfig.setRecommendedSupportedGroupsOnly(this.context.getLwM2MTransportConfigServer().isRecommendedSupportedGroups());
dtlsConfig.setRecommendedCipherSuitesOnly(this.context.getLwM2MTransportConfigServer().isRecommendedCiphers());
dtlsConfig.setServerOnly(true);
dtlsConfig.setRecommendedSupportedGroupsOnly(config.isRecommendedSupportedGroups());
dtlsConfig.setRecommendedCipherSuitesOnly(config.isRecommendedCiphers());
if (this.pskMode) {
dtlsConfig.setSupportedCipherSuites(
TLS_PSK_WITH_AES_128_CCM_8,
@ -124,27 +156,27 @@ public class LwM2mTransportServerConfiguration {
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256);
}
/** Set DTLS Config */
/* Set DTLS Config */
builder.setDtlsConfig(dtlsConfig);
/** Create LWM2M server */
/* Create LWM2M server */
return builder.build();
}
private void setServerWithCredentials(LeshanServerBuilder builder) {
try {
if (this.context.getLwM2MTransportConfigServer().getKeyStoreValue() != null) {
if (config.getKeyStoreValue() != null) {
if (this.setBuilderX509(builder)) {
X509Certificate rootCAX509Cert = (X509Certificate) this.context.getLwM2MTransportConfigServer().getKeyStoreValue().getCertificate(this.context.getLwM2MTransportConfigServer().getRootAlias());
X509Certificate rootCAX509Cert = (X509Certificate) config.getKeyStoreValue().getCertificate(config.getRootCertificateAlias());
if (rootCAX509Cert != null) {
X509Certificate[] trustedCertificates = new X509Certificate[1];
trustedCertificates[0] = rootCAX509Cert;
builder.setTrustedCertificates(trustedCertificates);
} else {
/** by default trust all */
/* by default trust all */
builder.setTrustedCertificates(new X509Certificate[0]);
}
/** Set securityStore with registrationStore*/
/* Set securityStore with registrationStore*/
builder.setAuthorizer(new DefaultAuthorizer(securityStore, new SecurityChecker() {
@Override
protected boolean matchX509Identity(String endpoint, String receivedX509CommonName,
@ -157,7 +189,7 @@ public class LwM2mTransportServerConfiguration {
this.infoPramsUri("RPK");
this.infoParamsServerKey(this.publicKey, this.privateKey);
} else {
/** by default trust all */
/* by default trust all */
builder.setTrustedCertificates(new X509Certificate[0]);
log.info("Unable to load X509 files for LWM2MServer");
this.pskMode = true;
@ -169,17 +201,11 @@ public class LwM2mTransportServerConfiguration {
}
private boolean setBuilderX509(LeshanServerBuilder builder) {
/**
* For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE
* For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks
*/
try {
X509Certificate serverCertificate = (X509Certificate) this.context.getLwM2MTransportConfigServer().getKeyStoreValue().getCertificate(this.context.getLwM2MTransportConfigServer().getServerAlias());
PrivateKey privateKey = (PrivateKey) this.context.getLwM2MTransportConfigServer().getKeyStoreValue().getKey(this.context.getLwM2MTransportConfigServer().getServerAlias(), this.context.getLwM2MTransportConfigServer().getKeyStorePasswordServer() == null ? null : this.context.getLwM2MTransportConfigServer().getKeyStorePasswordServer().toCharArray());
X509Certificate serverCertificate = (X509Certificate) config.getKeyStoreValue().getCertificate(config.getCertificateAlias());
PrivateKey privateKey = (PrivateKey) config.getKeyStoreValue().getKey(config.getCertificateAlias(), config.getKeyStorePassword() == null ? null : config.getKeyStorePassword().toCharArray());
PublicKey publicKey = serverCertificate.getPublicKey();
if (serverCertificate != null &&
privateKey != null && privateKey.getEncoded().length > 0 &&
publicKey != null && publicKey.getEncoded().length > 0) {
if (privateKey != null && privateKey.getEncoded().length > 0 && publicKey != null && publicKey.getEncoded().length > 0) {
builder.setPublicKey(serverCertificate.getPublicKey());
builder.setPrivateKey(privateKey);
builder.setCertificateChain(new X509Certificate[]{serverCertificate});
@ -206,10 +232,12 @@ public class LwM2mTransportServerConfiguration {
}
private void infoPramsUri(String mode) {
log.info("Server uses [{}]: serverNoSecureURI : [{}], serverSecureURI : [{}]",
mode,
this.context.getLwM2MTransportConfigServer().getServerHost() + ":" + this.context.getLwM2MTransportConfigServer().getServerPortNoSec(),
this.context.getLwM2MTransportConfigServer().getServerHostSecurity() + ":" + this.context.getLwM2MTransportConfigServer().getServerPortSecurity());
LwM2MTransportServerConfig lwM2MTransportServerConfig = config;
log.info("Server uses [{}]: serverNoSecureURI : [{}:{}], serverSecureURI : [{}:{}]", mode,
lwM2MTransportServerConfig.getHost(),
lwM2MTransportServerConfig.getPort(),
lwM2MTransportServerConfig.getSecureHost(),
lwM2MTransportServerConfig.getSecurePort());
}
private boolean setServerRPK(LeshanServerBuilder builder) {
@ -227,55 +255,42 @@ public class LwM2mTransportServerConfiguration {
return false;
}
/**
* From yml: server
* public_x: "${LWM2M_SERVER_PUBLIC_X:405354ea8893471d9296afbc8b020a5c6201b0bb25812a53b849d4480fa5f069}"
* public_y: "${LWM2M_SERVER_PUBLIC_Y:30c9237e946a3a1692c1cafaa01a238a077f632c99371348337512363f28212b}"
* private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED:274671fe40ce937b8a6352cf0a418e8a39e4bf0bb9bf74c910db953c20c73802}"
*/
private void generateKeyForRPK() throws NoSuchAlgorithmException, InvalidParameterSpecException, InvalidKeySpecException {
/** Get Elliptic Curve Parameter spec for secp256r1 */
/* Get Elliptic Curve Parameter spec for secp256r1 */
AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
algoParameters.init(new ECGenParameterSpec("secp256r1"));
ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
if (this.context.getLwM2MTransportConfigServer().getServerPublicX() != null &&
!this.context.getLwM2MTransportConfigServer().getServerPublicX().isEmpty() &&
this.context.getLwM2MTransportConfigServer().getServerPublicY() != null &&
!this.context.getLwM2MTransportConfigServer().getServerPublicY().isEmpty()) {
/** Get point values */
byte[] publicX = Hex.decodeHex(this.context.getLwM2MTransportConfigServer().getServerPublicX().toCharArray());
byte[] publicY = Hex.decodeHex(this.context.getLwM2MTransportConfigServer().getServerPublicY().toCharArray());
/** Create key specs */
LwM2MTransportServerConfig serverConfig = config;
if (StringUtils.isNotEmpty(serverConfig.getPublicX()) && StringUtils.isNotEmpty(serverConfig.getPublicY())) {
byte[] publicX = Hex.decodeHex(serverConfig.getPublicX().toCharArray());
byte[] publicY = Hex.decodeHex(serverConfig.getPublicY().toCharArray());
KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
parameterSpec);
/** Get keys */
this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
}
if (this.context.getLwM2MTransportConfigServer().getServerPrivateEncoded() != null &&
!this.context.getLwM2MTransportConfigServer().getServerPrivateEncoded().isEmpty()) {
/** Get private key */
byte[] privateS = Hex.decodeHex(this.context.getLwM2MTransportConfigServer().getServerPrivateEncoded().toCharArray());
String privateEncodedKey = serverConfig.getPrivateEncoded();
if (StringUtils.isNotEmpty(privateEncodedKey)) {
byte[] privateS = Hex.decodeHex(privateEncodedKey.toCharArray());
try {
this.privateKey = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(privateS));
} catch (InvalidKeySpecException ignore2) {
log.error("Invalid Server rpk.PrivateKey.getEncoded () [{}}]. PrivateKey has no EC algorithm", this.context.getLwM2MTransportConfigServer().getServerPrivateEncoded());
log.error("Invalid Server rpk.PrivateKey.getEncoded () [{}}]. PrivateKey has no EC algorithm", privateEncodedKey);
}
}
}
private void infoParamsServerKey(PublicKey publicKey, PrivateKey privateKey) {
/** Get x coordinate */
/* Get x coordinate */
byte[] x = ((ECPublicKey) publicKey).getW().getAffineX().toByteArray();
if (x[0] == 0)
x = Arrays.copyOfRange(x, 1, x.length);
/** Get Y coordinate */
/* Get Y coordinate */
byte[] y = ((ECPublicKey) publicKey).getW().getAffineY().toByteArray();
if (y[0] == 0)
y = Arrays.copyOfRange(y, 1, y.length);
/** Get Curves params */
/* Get Curves params */
String params = ((ECPublicKey) publicKey).getParams().toString();
String privHex = Hex.encodeHexString(privateKey.getEncoded());
log.info(" \n- Public Key (Hex): [{}] \n" +
@ -292,4 +307,9 @@ public class LwM2mTransportServerConfiguration {
params);
}
@Override
public String getName() {
return "LWM2M";
}
}

22
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java

@ -0,0 +1,22 @@
/**
* Copyright © 2016-2021 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.transport.lwm2m.server;
import org.thingsboard.server.common.data.TbTransportService;
public interface LwM2MTransportService extends TbTransportService {
}

99
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java

@ -0,0 +1,99 @@
/**
* Copyright © 2016-2021 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.transport.lwm2m.server;
import org.eclipse.californium.core.network.config.NetworkConfig;
public class LwM2mNetworkConfig {
public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort) {
NetworkConfig coapConfig = new NetworkConfig();
coapConfig.setInt(NetworkConfig.Keys.COAP_PORT,serverPortNoSec);
coapConfig.setInt(NetworkConfig.Keys.COAP_SECURE_PORT,serverSecurePort);
/*
Example:Property for large packet:
#NetworkConfig config = new NetworkConfig();
#config.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE,32);
#config.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE,32);
#config.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE,2048);
#config.setInt(NetworkConfig.Keys.MAX_RETRANSMIT,3);
#config.setInt(NetworkConfig.Keys.MAX_TRANSMIT_WAIT,120000);
*/
/*
Property to indicate if the response should always include the Block2 option \
when client request early blockwise negociation but the response can be sent on one packet.
- value of false indicate that the server will respond without block2 option if no further blocks are required.
- value of true indicate that the server will response with block2 option event if no further blocks are required.
CoAP client will try to use block mode
or adapt the block size when receiving a 4.13 Entity too large response code
*/
coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_STRICT_BLOCK2_OPTION, true);
/*
Property to indicate if the response should always include the Block2 option \
when client request early blockwise negociation but the response can be sent on one packet.
- value of false indicate that the server will respond without block2 option if no further blocks are required.
- value of true indicate that the server will response with block2 option event if no further blocks are required.
*/
coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true);
coapConfig.setInt(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, 300000);
/*
!!! REQUEST_ENTITY_TOO_LARGE CODE=4.13
The maximum size of a resource body (in bytes) that will be accepted
as the payload of a POST/PUT or the response to a GET request in a
transparent> blockwise transfer.
This option serves as a safeguard against excessive memory
consumption when many resources contain large bodies that cannot be
transferred in a single CoAP message. This option has no impact on
*manually* managed blockwise transfers in which the blocks are handled individually.
Note that this option does not prevent local clients or resource
implementations from sending large bodies as part of a request or response to a peer.
The default value of this property is DEFAULT_MAX_RESOURCE_BODY_SIZE = 8192
A value of {@code 0} turns off transparent handling of blockwise transfers altogether.
*/
coapConfig.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 256 * 1024 * 1024);
/*
The default DTLS response matcher.
Supported values are STRICT, RELAXED, or PRINCIPAL.
The default value is STRICT.
Create new instance of udp endpoint context matcher.
Params:
checkAddress
true with address check, (STRICT, UDP)
- false, without
*/
coapConfig.setString(NetworkConfig.Keys.RESPONSE_MATCHING, "STRICT");
/*
https://tools.ietf.org/html/rfc7959#section-2.9.3
The block size (number of bytes) to use when doing a blockwise transfer. \
This value serves as the upper limit for block size in blockwise transfers
*/
coapConfig.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE, 1024);
/*
The maximum payload size (in bytes) that can be transferred in a
single message, i.e. without requiring a blockwise transfer.
NB: this value MUST be adapted to the maximum message size supported by the transport layer.
In particular, this value cannot exceed the network's MTU if UDP is used as the transport protocol
DEFAULT_VALUE = 1024
*/
coapConfig.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 1024);
coapConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 4);
return coapConfig;
}
}

20
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java

@ -26,14 +26,15 @@ import org.eclipse.leshan.server.registration.RegistrationUpdate;
import java.util.Collection;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromObjectIdToIdVer;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_INFO;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.convertPathFromObjectIdToIdVer;
@Slf4j
public class LwM2mServerListener {
private final LwM2mTransportServiceImpl service;
private final LwM2mTransportMsgHandler service;
public LwM2mServerListener(LwM2mTransportServiceImpl service) {
public LwM2mServerListener(LwM2mTransportMsgHandler service) {
this.service = service;
}
@ -85,17 +86,19 @@ public class LwM2mServerListener {
@Override
public void cancelled(Observation observation) {
log.info("Received notification cancelled from [{}] ", observation.getPath());
String msg = String.format("%s: Cancel Observation %s.", LOG_LW2M_INFO, observation.getPath());
service.sendLogsToThingsboard(msg, observation.getRegistrationId());
log.trace(msg);
}
@Override
public void onResponse(Observation observation, Registration registration, ObserveResponse response) {
if (registration != null) {
try {
service.onObservationResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(),
service.onUpdateValueAfterReadResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(),
registration), response, null);
} catch (Exception e) {
log.error("[{}] onResponse", e.toString());
log.error("Observation/Read onResponse", e);
}
}
@ -108,7 +111,10 @@ public class LwM2mServerListener {
@Override
public void newObservation(Observation observation, Registration registration) {
log.info("Received newObservation from [{}] endpoint [{}] ", observation.getPath(), registration.getEndpoint());
String msg = String.format("%s: Successful start newObservation %s.", LOG_LW2M_INFO,
observation.getPath());
service.sendLogsToThingsboard(msg, registration.getId());
log.trace(msg);
}
};

4
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java

@ -35,10 +35,10 @@ import java.util.Optional;
@Slf4j
public class LwM2mSessionMsgListener implements GenericFutureListener<Future<? super Void>>, SessionMsgListener {
private LwM2mTransportServiceImpl service;
private DefaultLwM2MTransportMsgHandler service;
private TransportProtos.SessionInfoProto sessionInfo;
public LwM2mSessionMsgListener(LwM2mTransportServiceImpl service, TransportProtos.SessionInfoProto sessionInfo) {
public LwM2mSessionMsgListener(DefaultLwM2MTransportMsgHandler service, TransportProtos.SessionInfoProto sessionInfo) {
this.service = service;
this.sessionInfo = sessionInfo;
}

32
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContext.java

@ -0,0 +1,32 @@
/**
* Copyright © 2016-2021 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.transport.lwm2m.server;
import lombok.Getter;
import lombok.Setter;
import org.eclipse.leshan.server.californium.LeshanServer;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.transport.TransportContext;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
@Component
@TbLwM2mTransportComponent
public class LwM2mTransportContext extends TransportContext {
@Getter @Setter
private LeshanServer server;
}

37
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportHandler.java → common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportHandlerUtil.java

@ -25,7 +25,6 @@ import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.californium.core.network.config.NetworkConfig;
import org.eclipse.leshan.core.attributes.Attribute;
import org.eclipse.leshan.core.attributes.AttributeSet;
import org.eclipse.leshan.core.model.ObjectModel;
@ -40,7 +39,6 @@ import org.eclipse.leshan.core.node.codec.CodecException;
import org.eclipse.leshan.core.request.DownlinkRequest;
import org.eclipse.leshan.core.request.WriteAttributesRequest;
import org.eclipse.leshan.core.util.Hex;
import org.eclipse.leshan.server.californium.LeshanServerBuilder;
import org.eclipse.leshan.server.registration.Registration;
import org.nustaq.serialization.FSTConfiguration;
import org.thingsboard.server.common.data.DeviceProfile;
@ -50,7 +48,6 @@ import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
@ -70,9 +67,8 @@ import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPA
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
@Slf4j
public class LwM2mTransportHandler {
public class LwM2mTransportHandlerUtil {
// public static final String BASE_DEVICE_API_TOPIC = "v1/devices/me";
public static final String TRANSPORT_DEFAULT_LWM2M_VERSION = "1.0";
public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings";
public static final String BOOTSTRAP = "bootstrap";
@ -85,19 +81,12 @@ public class LwM2mTransportHandler {
public static final String KEY_NAME = "keyName";
public static final String OBSERVE_LWM2M = "observe";
public static final String ATTRIBUTE_LWM2M = "attributeLwm2m";
// public static final String RESOURCE_VALUE = "resValue";
// public static final String RESOURCE_TYPE = "resType";
private static final String REQUEST = "/request";
// private static final String RESPONSE = "/response";
private static final String ATTRIBUTES = "/" + ATTRIBUTE;
public static final String TELEMETRIES = "/" + TELEMETRY;
// public static final String ATTRIBUTES_RESPONSE = ATTRIBUTES + RESPONSE;
public static final String ATTRIBUTES_REQUEST = ATTRIBUTES + REQUEST;
// public static final String DEVICE_ATTRIBUTES_RESPONSE = ATTRIBUTES_RESPONSE + "/";
public static final String DEVICE_ATTRIBUTES_REQUEST = ATTRIBUTES_REQUEST + "/";
// public static final String DEVICE_ATTRIBUTES_TOPIC = BASE_DEVICE_API_TOPIC + ATTRIBUTES;
// public static final String DEVICE_TELEMETRY_TOPIC = BASE_DEVICE_API_TOPIC + TELEMETRIES;
public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms
@ -112,6 +101,11 @@ public class LwM2mTransportHandler {
public static final String CLIENT_NOT_AUTHORIZED = "Client not authorized";
public static final Integer FR_OBJECT_ID = 5;
public static final Integer FR_RESOURCE_VER_ID = 7;
public static final String FR_PATH_RESOURCE_VER_ID = LWM2M_SEPARATOR_PATH + FR_OBJECT_ID + LWM2M_SEPARATOR_PATH
+ "0" + LWM2M_SEPARATOR_PATH + FR_RESOURCE_VER_ID;
public enum LwM2mTypeServer {
BOOTSTRAP(0, "bootstrap"),
CLIENT(1, "client");
@ -168,6 +162,8 @@ public class LwM2mTransportHandler {
WRITE_ATTRIBUTES(8, "WriteAttributes"),
DELETE(9, "Delete");
// READ_INFO_FW(10, "ReadInfoFirmware");
public int code;
public String type;
@ -190,21 +186,6 @@ public class LwM2mTransportHandler {
public static final String SERVICE_CHANNEL = "SERVICE";
public static final String RESPONSE_CHANNEL = "RESP";
public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort) {
NetworkConfig coapConfig;
File configFile = new File(NetworkConfig.DEFAULT_FILE_NAME);
if (configFile.isFile()) {
coapConfig = new NetworkConfig();
coapConfig.load(configFile);
} else {
coapConfig = LeshanServerBuilder.createDefaultNetworkConfig();
coapConfig.store(configFile);
}
coapConfig.setString("COAP_PORT", Integer.toString(serverPortNoSec));
coapConfig.setString("COAP_SECURE_PORT", Integer.toString(serverSecurePort));
return coapConfig;
}
public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath resourcePath) throws CodecException {
switch (type) {
case BOOLEAN:
@ -275,7 +256,7 @@ public class LwM2mTransportHandler {
ObjectMapper mapper = new ObjectMapper();
String profileStr = mapper.writeValueAsString(profile);
JsonObject profileJson = (profileStr != null) ? validateJson(profileStr) : null;
return getValidateCredentialsBodyFromThingsboard(profileJson) ? LwM2mTransportHandler.getNewProfileParameters(profileJson, deviceProfile.getTenantId()) : null;
return getValidateCredentialsBodyFromThingsboard(profileJson) ? LwM2mTransportHandlerUtil.getNewProfileParameters(profileJson, deviceProfile.getTenantId()) : null;
} catch (IOException e) {
log.error("", e);
}

6
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportService.java → common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportMsgHandler.java

@ -27,7 +27,7 @@ import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcReques
import java.util.Collection;
import java.util.Optional;
public interface LwM2mTransportService extends TbTransportService {
public interface LwM2mTransportMsgHandler {
void onRegistered(Registration registration, Collection<Observation> previousObsersations);
@ -39,7 +39,7 @@ public interface LwM2mTransportService extends TbTransportService {
void setCancelObservations(Registration registration);
void onObservationResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest);
void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest);
void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo);
@ -61,5 +61,7 @@ public interface LwM2mTransportService extends TbTransportService {
void doDisconnect(TransportProtos.SessionInfoProto sessionInfo);
void onAwakeDev(Registration registration);
void sendLogsToThingsboard(String msg, String registrationId);
}

205
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.transport.lwm2m.server;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.coap.CoAP;
@ -50,6 +51,7 @@ import org.eclipse.leshan.server.registration.Registration;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest;
@ -66,50 +68,38 @@ import java.util.stream.Collectors;
import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT;
import static org.eclipse.leshan.core.ResponseCode.BAD_REQUEST;
import static org.eclipse.leshan.core.ResponseCode.NOT_FOUND;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEFAULT_TIMEOUT;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_VALUE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.OBSERVE_CANCEL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.OBSERVE_READ_ALL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.RESPONSE_CHANNEL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromIdVerToObjectId;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromObjectIdToIdVer;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.createWriteAttributeRequest;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.DEFAULT_TIMEOUT;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.FR_PATH_RESOURCE_VER_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_ERROR;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_INFO;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_VALUE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.OBSERVE_CANCEL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.OBSERVE_READ_ALL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.RESPONSE_CHANNEL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.convertPathFromIdVerToObjectId;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.convertPathFromObjectIdToIdVer;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.createWriteAttributeRequest;
@Slf4j
@Service
@TbLwM2mTransportComponent
@RequiredArgsConstructor
public class LwM2mTransportRequest {
private ExecutorService executorResponse;
public LwM2mValueConverterImpl converter;
private final LwM2mTransportContextServer lwM2mTransportContextServer;
private final LwM2mTransportContext context;
private final LwM2MTransportServerConfig config;
private final LwM2mTransportServerHelper lwM2MTransportServerHelper;
private final LwM2mClientContext lwM2mClientContext;
private final LeshanServer leshanServer;
private final LwM2mTransportServiceImpl serviceImpl;
private final TransportService transportService;
public LwM2mTransportRequest(LwM2mTransportContextServer lwM2mTransportContextServer,
LwM2mClientContext lwM2mClientContext, LeshanServer leshanServer,
LwM2mTransportServiceImpl serviceImpl, TransportService transportService) {
this.lwM2mTransportContextServer = lwM2mTransportContextServer;
this.lwM2mClientContext = lwM2mClientContext;
this.leshanServer = leshanServer;
this.serviceImpl = serviceImpl;
this.transportService = transportService;
}
private final DefaultLwM2MTransportMsgHandler serviceImpl;
@PostConstruct
public void init() {
this.converter = LwM2mValueConverterImpl.getInstance();
executorResponse = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getResponsePoolSize(),
executorResponse = Executors.newFixedThreadPool(this.config.getResponsePoolSize(),
new NamedThreadFactory(String.format("LwM2M %s channel response", RESPONSE_CHANNEL)));
}
@ -125,7 +115,6 @@ public class LwM2mTransportRequest {
public void sendAllRequest(Registration registration, String targetIdVer, LwM2mTypeOper typeOper,
String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) {
try {
String target = convertPathFromIdVerToObjectId(targetIdVer);
DownlinkRequest request = null;
ContentFormat contentFormat = contentFormatName != null ? ContentFormat.fromName(contentFormatName.toUpperCase()) : ContentFormat.DEFAULT;
@ -145,11 +134,11 @@ public class LwM2mTransportRequest {
break;
case OBSERVE:
if (resultIds.isResource()) {
request = new ObserveRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId());
request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId());
} else if (resultIds.isObjectInstance()) {
request = new ObserveRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId());
request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId());
} else if (resultIds.getObjectId() >= 0) {
request = new ObserveRequest(resultIds.getObjectId());
request = new ObserveRequest(contentFormat, resultIds.getObjectId());
}
break;
case OBSERVE_CANCEL:
@ -158,10 +147,10 @@ public class LwM2mTransportRequest {
* At server side this will not remove the observation from the observation store, to do it you need to use
* {@code ObservationService#cancelObservation()}
*/
leshanServer.getObservationService().cancelObservations(registration, target);
context.getServer().getObservationService().cancelObservations(registration, target);
break;
case EXECUTE:
resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer()
resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.config
.getModelProvider());
if (params != null && !resourceModel.multiple) {
request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resourceModel.type, ResourceModel.Type.STRING, resultIds));
@ -171,9 +160,7 @@ public class LwM2mTransportRequest {
break;
case WRITE_REPLACE:
// Request to write a <b>String Single-Instance Resource</b> using the TLV content format.
// resource = lwM2MClient.getResourceModel(targetIdVer);
// if (contentFormat.equals(ContentFormat.TLV) && !resource.multiple) {
resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer()
resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.config
.getModelProvider());
if (contentFormat.equals(ContentFormat.TLV)) {
request = this.getWriteRequestSingleResource(null, resultIds.getObjectId(),
@ -181,7 +168,6 @@ public class LwM2mTransportRequest {
registration, rpcRequest);
}
// Mode.REPLACE && Request to write a <b>String Single-Instance Resource</b> using the given content format (TEXT, TLV, JSON)
// else if (!contentFormat.equals(ContentFormat.TLV) && !resource.multiple) {
else if (!contentFormat.equals(ContentFormat.TLV)) {
request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(),
resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModel.type,
@ -215,13 +201,16 @@ public class LwM2mTransportRequest {
long finalTimeoutInMs = timeoutInMs;
lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, rpcRequest));
} catch (Exception e) {
log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper, e);
log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper.name(), e);
}
} else if (OBSERVE_CANCEL == typeOper) {
log.trace("[{}], [{}] - [{}] SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer);
if (rpcRequest != null) {
rpcRequest.setInfoMsg(null);
serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), null, null);
}
} else if (OBSERVE_CANCEL == typeOper && rpcRequest != null) {
rpcRequest.setInfoMsg(null);
serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), null, null);
} else {
log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper, targetIdVer);
log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer);
if (rpcRequest != null) {
String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null";
serviceImpl.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
@ -232,12 +221,12 @@ public class LwM2mTransportRequest {
serviceImpl.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
}
} else if (OBSERVE_READ_ALL.name().equals(typeOper.name())) {
Set<Observation> observations = leshanServer.getObservationService().getObservations(registration);
Set<Observation> observations = context.getServer().getObservationService().getObservations(registration);
Set<String> observationPaths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet());
String msg = String.format("%s: type operation %s observation paths - %s", LOG_LW2M_INFO,
OBSERVE_READ_ALL.type, observationPaths);
serviceImpl.sendLogsToThingsboard(msg, registration);
log.info("[{}], [{}]", registration.getEndpoint(), msg);
serviceImpl.sendLogsToThingsboard(msg, registration.getId());
log.trace("[{}] [{}], [{}]", typeOper.name(), registration.getEndpoint(), msg);
if (rpcRequest != null) {
String valueMsg = String.format("Observation paths - %s", observationPaths);
serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE);
@ -246,7 +235,7 @@ public class LwM2mTransportRequest {
} catch (Exception e) {
String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR,
typeOper.name(), e.getMessage());
serviceImpl.sendLogsToThingsboard(msg, registration);
serviceImpl.sendLogsToThingsboard(msg, registration.getId());
throw new Exception(e);
}
}
@ -259,47 +248,55 @@ public class LwM2mTransportRequest {
@SuppressWarnings("unchecked")
private void sendRequest(Registration registration, LwM2mClient lwM2MClient, DownlinkRequest request, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) {
leshanServer.send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> {
context.getServer().send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> {
if (!lwM2MClient.isInit()) {
lwM2MClient.initValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
}
if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) {
this.handleResponse(registration, request.getPath().toString(), response, request, rpcRequest);
if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest()) {
LwM2mNode node = ((WriteRequest) request).getNode();
Object value = this.converter.convertValue(((LwM2mSingleResource) node).getValue(),
((LwM2mSingleResource) node).getType(), ResourceModel.Type.STRING, request.getPath());
String msg = String.format("%s: sendRequest Replace: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s SendRequest to Client",
LOG_LW2M_INFO, ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(),
response.getCode().getName(), request.getPath().toString(), value);
serviceImpl.sendLogsToThingsboard(msg, registration);
log.info("[{}] [{}] - [{}] [{}] Update SendRequest[{}]", registration.getEndpoint(),
((Response) response.getCoapResponse()).getCode(), response.getCode(),
request.getPath().toString(), value);
serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, LOG_LW2M_INFO);
}
} else {
String msg = String.format("%s: sendRequest: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s SendRequest to Client", LOG_LW2M_ERROR,
String msg = String.format("%s: SendRequest %s: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s", LOG_LW2M_ERROR, request.getClass().getName().toString(),
((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString());
serviceImpl.sendLogsToThingsboard(msg, registration);
log.error("[{}], [{}] - [{}] [{}] error SendRequest", registration.getEndpoint(), ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString());
serviceImpl.sendLogsToThingsboard(msg, registration.getId());
log.error("[{}] [{}], [{}] - [{}] [{}] error SendRequest", request.getClass().getName().toString(), registration.getEndpoint(),
((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString());
if (!lwM2MClient.isInit()) {
lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
}
if (rpcRequest != null) {
serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR);
}
/** Not Found
* set setClient_fw_version = empty
**/
if (FR_PATH_RESOURCE_VER_ID.equals(request.getPath().toString()) && lwM2MClient.isUpdateFw()) {
lwM2MClient.setUpdateFw(false);
lwM2MClient.getFrUpdate().setClientFwVersion("");
log.warn("updateFirmwareClient1");
serviceImpl.updateFirmwareClient(lwM2MClient);
}
}
}, e -> {
/** version == null
* set setClient_fw_version = empty
**/
if (FR_PATH_RESOURCE_VER_ID.equals(request.getPath().toString()) && lwM2MClient.isUpdateFw()) {
lwM2MClient.setUpdateFw(false);
lwM2MClient.getFrUpdate().setClientFwVersion("");
log.warn("updateFirmwareClient2");
serviceImpl.updateFirmwareClient(lwM2MClient);
}
if (!lwM2MClient.isInit()) {
lwM2MClient.initValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
}
String msg = String.format("%s: sendRequest: Resource path - %s msg error - %s SendRequest to Client",
LOG_LW2M_ERROR, request.getPath().toString(), e.getMessage());
serviceImpl.sendLogsToThingsboard(msg, registration);
log.error("[{}] - [{}] error SendRequest", request.getPath().toString(), e.toString());
String msg = String.format("%s: SendRequest %s: Resource path - %s msg error - %s",
LOG_LW2M_ERROR, request.getClass().getName().toString(), request.getPath().toString(), e.getMessage());
serviceImpl.sendLogsToThingsboard(msg, registration.getId());
log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString());
if (rpcRequest != null) {
serviceImpl.sentRpcRequest(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR);
}
});
}
private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId,
@ -323,7 +320,9 @@ public class LwM2mTransportRequest {
Date date = new Date(Long.decode(value.toString()));
return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, date) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, date);
case OPAQUE: // byte[] value, base64
return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray()));
byte[] valueRequest = value instanceof byte[] ? (byte[]) value : Hex.decodeHex(value.toString().toCharArray());
return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, valueRequest) :
new WriteRequest(contentFormat, objectId, instanceId, resourceId, valueRequest);
default:
}
}
@ -337,7 +336,7 @@ public class LwM2mTransportRequest {
String patn = "/" + objectId + "/" + instanceId + "/" + resourceId;
String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client",
patn, type, value, e.toString());
serviceImpl.sendLogsToThingsboard(msg, registration);
serviceImpl.sendLogsToThingsboard(msg, registration.getId());
log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString());
if (rpcRequest != null) {
String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value);
@ -369,7 +368,7 @@ public class LwM2mTransportRequest {
DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
String pathIdVer = convertPathFromObjectIdToIdVer(path, registration);
if (response instanceof ReadResponse) {
serviceImpl.onObservationResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest);
serviceImpl.onUpdateValueAfterReadResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest);
} else if (response instanceof CancelObservationResponse) {
log.info("[{}] Path [{}] CancelObservationResponse 3_Send", pathIdVer, response);
@ -389,14 +388,56 @@ public class LwM2mTransportRequest {
} else if (response instanceof WriteAttributesResponse) {
log.info("[{}] Path [{}] WriteAttributesResponse 8_Send", pathIdVer, response);
} else if (response instanceof WriteResponse) {
log.info("[{}] Path [{}] WriteAttributesResponse 9_Send", pathIdVer, response);
log.info("[{}] Path [{}] WriteResponse 9_Send", pathIdVer, response);
this.infoWriteResponse(registration, response, request);
serviceImpl.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request);
}
if (rpcRequest != null && (response instanceof ExecuteResponse
|| response instanceof WriteAttributesResponse
|| response instanceof DeleteResponse)) {
rpcRequest.setInfoMsg(null);
serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, null);
if (rpcRequest != null) {
if (response instanceof ExecuteResponse
|| response instanceof WriteAttributesResponse
|| response instanceof DeleteResponse) {
rpcRequest.setInfoMsg(null);
serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, null);
} else if (response instanceof WriteResponse) {
serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, LOG_LW2M_INFO);
}
}
}
private void infoWriteResponse(Registration registration, LwM2mResponse response, DownlinkRequest request) {
try {
LwM2mNode node = ((WriteRequest) request).getNode();
String msg;
Object value;
LwM2mSingleResource singleResource = (LwM2mSingleResource) node;
if (singleResource.getType() == ResourceModel.Type.STRING || singleResource.getType() == ResourceModel.Type.OPAQUE) {
int valueLength;
if (singleResource.getType() == ResourceModel.Type.STRING) {
valueLength = ((String) singleResource.getValue()).length();
value = ((String) singleResource.getValue())
.substring(Math.min(valueLength, config.getLogMaxLength()));
} else {
valueLength = ((byte[]) singleResource.getValue()).length;
value = new String(Arrays.copyOf(((byte[]) singleResource.getValue()),
Math.min(valueLength, config.getLogMaxLength())));
}
value = valueLength > config.getLogMaxLength() ? value + "..." : value;
msg = String.format("%s: Update finished successfully: Lwm2m code - %d Resource path - %s length - %s value - %s",
LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), valueLength, value);
} else {
value = this.converter.convertValue(singleResource.getValue(),
singleResource.getType(), ResourceModel.Type.STRING, request.getPath());
msg = String.format("%s: Update finished successfully: Lwm2m code - %d Resource path - %s value - %s",
LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), value);
}
if (msg != null) {
serviceImpl.sendLogsToThingsboard(msg, registration.getId());
log.warn("[{}] [{}] [{}] - [{}] [{}] Update finished successfully: [{}]", request.getClass().getName(), registration.getEndpoint(),
((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString(), value);
}
} catch (Exception e) {
log.trace("Fail convert value from request to string. ", e);
}
}
}

85
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContextServer.java → common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java

@ -31,6 +31,7 @@ package org.thingsboard.server.transport.lwm2m.server;
*/
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.model.DDFFileParser;
import org.eclipse.leshan.core.model.DefaultDDFFileValidator;
@ -39,11 +40,8 @@ import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.model.ResourceModel;
import org.eclipse.leshan.core.node.codec.CodecException;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.transport.TransportContext;
import org.thingsboard.server.common.transport.TransportResourceCache;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigServer;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg;
@ -57,39 +55,21 @@ import java.util.ArrayList;
import java.util.List;
import static org.thingsboard.server.gen.transport.TransportProtos.KeyValueType.BOOLEAN_V;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_TELEMETRY;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_TELEMETRY;
@Slf4j
@Component
@TbLwM2mTransportComponent
public class LwM2mTransportContextServer extends TransportContext {
@RequiredArgsConstructor
public class LwM2mTransportServerHelper {
private final LwM2MTransportConfigServer lwM2MTransportConfigServer;
private final LwM2mTransportContext context;
private final TransportService transportService;
private final TransportResourceCache transportResourceCache;
@Getter
private final LwM2MJsonAdaptor adaptor;
public LwM2mTransportContextServer(LwM2MTransportConfigServer lwM2MTransportConfigServer, TransportService transportService, TransportResourceCache transportResourceCache, LwM2MJsonAdaptor adaptor) {
this.lwM2MTransportConfigServer = lwM2MTransportConfigServer;
this.transportService = transportService;
this.transportResourceCache = transportResourceCache;
this.adaptor = adaptor;
}
public LwM2MTransportConfigServer getLwM2MTransportConfigServer() {
return this.lwM2MTransportConfigServer;
}
public TransportResourceCache getTransportResourceCache() {
return this.transportResourceCache;
}
/**
* send to Thingsboard Attribute || Telemetry
*
@ -134,7 +114,7 @@ public class LwM2mTransportContextServer extends TransportContext {
*/
public SessionInfoProto getValidateSessionInfo(TransportProtos.ValidateDeviceCredentialsResponseMsg msg, long mostSignificantBits, long leastSignificantBits) {
return SessionInfoProto.newBuilder()
.setNodeId(this.getNodeId())
.setNodeId(context.getNodeId())
.setSessionIdMSB(mostSignificantBits)
.setSessionIdLSB(leastSignificantBits)
.setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB())
@ -165,8 +145,8 @@ public class LwM2mTransportContextServer extends TransportContext {
* @param logMsg - info about Logs
* @return- KeyValueProto for telemetry (Logs)
*/
public List <TransportProtos.KeyValueProto> getKvLogyToThingsboard(String logMsg) {
List <TransportProtos.KeyValueProto> result = new ArrayList<>();
public List<TransportProtos.KeyValueProto> getKvLogyToThingsboard(String logMsg) {
List<TransportProtos.KeyValueProto> result = new ArrayList<>();
result.add(TransportProtos.KeyValueProto.newBuilder()
.setKey(LOG_LW2M_TELEMETRY)
.setType(TransportProtos.KeyValueType.STRING_V)
@ -179,32 +159,31 @@ public class LwM2mTransportContextServer extends TransportContext {
* @throws CodecException -
*/
public TransportProtos.KeyValueProto getKvAttrTelemetryToThingsboard(ResourceModel.Type resourceType, String resourceName, Object value, boolean isMultiInstances) {
TransportProtos.KeyValueProto.Builder kvProto = TransportProtos.KeyValueProto.newBuilder().setKey(resourceName);
if (isMultiInstances) {
kvProto.setType(TransportProtos.KeyValueType.JSON_V)
.setJsonV((String) value);
public TransportProtos.KeyValueProto getKvAttrTelemetryToThingsboard(ResourceModel.Type resourceType, String resourceName, Object value, boolean isMultiInstances) {
TransportProtos.KeyValueProto.Builder kvProto = TransportProtos.KeyValueProto.newBuilder().setKey(resourceName);
if (isMultiInstances) {
kvProto.setType(TransportProtos.KeyValueType.JSON_V)
.setJsonV((String) value);
} else {
switch (resourceType) {
case BOOLEAN:
kvProto.setType(BOOLEAN_V).setBoolV((Boolean) value).build();
break;
case STRING:
case TIME:
case OPAQUE:
case OBJLNK:
kvProto.setType(TransportProtos.KeyValueType.STRING_V).setStringV((String) value);
break;
case INTEGER:
kvProto.setType(TransportProtos.KeyValueType.LONG_V).setLongV((Long) value);
break;
case FLOAT:
kvProto.setType(TransportProtos.KeyValueType.DOUBLE_V).setDoubleV((Double) value);
}
else {
switch (resourceType) {
case BOOLEAN:
kvProto.setType(BOOLEAN_V).setBoolV((Boolean) value).build();
break;
case STRING:
case TIME:
case OPAQUE:
case OBJLNK:
kvProto.setType(TransportProtos.KeyValueType.STRING_V).setStringV((String) value);
break;
case INTEGER:
kvProto.setType(TransportProtos.KeyValueType.LONG_V).setLongV((Long) value);
break;
case FLOAT:
kvProto.setType(TransportProtos.KeyValueType.DOUBLE_V).setDoubleV((Double) value);
}
}
return kvProto.build();
}
return kvProto.build();
}
/**
*
@ -230,7 +209,7 @@ public class LwM2mTransportContextServer extends TransportContext {
throw new CodecException("Invalid ResourceModel_Type for resource %s, got %s", resourcePath, currentType);
}
public Object getValueFromKvProto (TransportProtos.KeyValueProto kv) {
public Object getValueFromKvProto(TransportProtos.KeyValueProto kv) {
switch (kv.getType()) {
case BOOLEAN_V:
return kv.getBoolV();

31
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerInitializer.java

@ -30,35 +30,4 @@ import javax.annotation.PreDestroy;
@TbLwM2mTransportComponent
public class LwM2mTransportServerInitializer {
@Autowired
private LwM2mTransportServiceImpl service;
@Autowired
private LeshanServer leshanServer;
@Autowired
private LwM2mTransportContextServer context;
@PostConstruct
public void init() {
if (this.context.getLwM2MTransportConfigServer().getEnableGenNewKeyPskRpk()) {
new LWM2MGenerationPSkRPkECC();
}
this.startLhServer();
}
private void startLhServer() {
this.leshanServer.start();
LwM2mServerListener lhServerCertListener = new LwM2mServerListener(service);
this.leshanServer.getRegistrationService().addListener(lhServerCertListener.registrationListener);
this.leshanServer.getPresenceService().addListener(lhServerCertListener.presenceListener);
this.leshanServer.getObservationService().addListener(lhServerCertListener.observationListener);
}
@PreDestroy
public void shutdown() {
log.info("Stopping LwM2M transport Server!");
leshanServer.destroy();
log.info("LwM2M transport Server stopped!");
}
}

16
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.transport.lwm2m.server;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.model.DefaultDDFFileValidator;
import org.eclipse.leshan.core.model.LwM2mModel;
@ -37,6 +38,7 @@ import static org.thingsboard.server.common.data.ResourceType.LWM2M_MODEL;
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY;
@Slf4j
@RequiredArgsConstructor
public class LwM2mVersionedModelProvider implements LwM2mModelProvider {
/**
@ -46,12 +48,8 @@ public class LwM2mVersionedModelProvider implements LwM2mModelProvider {
* Value = TenantId
*/
private final LwM2mClientContext lwM2mClientContext;
private final LwM2mTransportContextServer lwM2mTransportContextServer;
public LwM2mVersionedModelProvider(LwM2mClientContext lwM2mClientContext, LwM2mTransportContextServer lwM2mTransportContextServer) {
this.lwM2mClientContext = lwM2mClientContext;
this.lwM2mTransportContextServer = lwM2mTransportContextServer;
}
private final LwM2mTransportServerHelper helper;
private final LwM2mTransportContext context;
private String getKeyIdVer(Integer objectId, String version) {
return objectId != null ? objectId + LWM2M_SEPARATOR_KEY + ((version == null || version.isEmpty()) ? ObjectModel.DEFAULT_VERSION : version) : null;
@ -120,11 +118,9 @@ public class LwM2mVersionedModelProvider implements LwM2mModelProvider {
private ObjectModel getObjectModelDynamic(Integer objectId, String version) {
String key = getKeyIdVer(objectId, version);
Optional<TbResource> tbResource = lwM2mTransportContextServer
.getTransportResourceCache()
.get(this.tenantId, LWM2M_MODEL, key);
Optional<TbResource> tbResource = context.getTransportResourceCache().get(this.tenantId, LWM2M_MODEL, key);
return tbResource.map(resource -> lwM2mTransportContextServer.parseFromXmlToObjectModel(
return tbResource.map(resource -> helper.parseFromXmlToObjectModel(
Base64.getDecoder().decode(resource.getData()),
key + ".xml",
new DefaultDDFFileValidator())).orElse(null);

21
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MJsonAdaptor.java

@ -24,11 +24,8 @@ import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.gen.transport.TransportProtos;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Collection;
import java.util.Random;
import java.util.Set;
@Slf4j
@Component("LwM2MJsonAdaptor")
@ -54,11 +51,7 @@ public class LwM2MJsonAdaptor implements LwM2MTransportAdaptor {
}
@Override
public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(List<String> clientKeys, List<String> sharedKeys) throws AdaptorException {
return processGetAttributeRequestMsg(clientKeys, sharedKeys);
}
protected TransportProtos.GetAttributeRequestMsg processGetAttributeRequestMsg(List<String> clientKeys, List<String> sharedKeys) throws AdaptorException {
public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(Collection<String> clientKeys, Collection<String> sharedKeys) throws AdaptorException {
try {
TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder();
Random random = new Random();
@ -75,14 +68,4 @@ public class LwM2MJsonAdaptor implements LwM2MTransportAdaptor {
throw new AdaptorException(e);
}
}
private Set<String> toStringSet(JsonElement requestBody, String name) {
JsonElement element = requestBody.getAsJsonObject().get(name);
if (element != null) {
return new HashSet<>(Arrays.asList(element.getAsString().split(",")));
} else {
return null;
}
}
}

4
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MTransportAdaptor.java

@ -19,7 +19,7 @@ import com.google.gson.JsonElement;
import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.gen.transport.TransportProtos;
import java.util.List;
import java.util.Collection;
public interface LwM2MTransportAdaptor {
@ -27,5 +27,5 @@ public interface LwM2MTransportAdaptor {
TransportProtos.PostAttributeMsg convertToPostAttributes(JsonElement jsonElement) throws AdaptorException;
TransportProtos.GetAttributeRequestMsg convertToGetAttributes(List<String> clientKeys, List<String> sharedKeys) throws AdaptorException;
TransportProtos.GetAttributeRequestMsg convertToGetAttributes(Collection<String> clientKeys, Collection<String> sharedKeys) throws AdaptorException;
}

26
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java

@ -27,7 +27,7 @@ import org.eclipse.leshan.server.security.SecurityInfo;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
import org.thingsboard.server.transport.lwm2m.server.LwM2mQueuedRequest;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServiceImpl;
import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MTransportMsgHandler;
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
import java.util.Collection;
@ -42,9 +42,9 @@ import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.TRANSPORT_DEFAULT_LWM2M_VERSION;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromIdVerToObjectId;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getVerFromPathIdVerOrId;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.TRANSPORT_DEFAULT_LWM2M_VERSION;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.convertPathFromIdVerToObjectId;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.getVerFromPathIdVerOrId;
@Slf4j
@Data
@ -57,13 +57,15 @@ public class LwM2mClient implements Cloneable {
private UUID deviceId;
private UUID sessionId;
private UUID profileId;
private volatile LwM2mFirmwareUpdate frUpdate;
private Registration registration;
private ValidateDeviceCredentialsResponseMsg credentialsResponse;
private final Map<String, ResourceValue> resources;
private final Map<String, TransportProtos.TsKvProto> delayedRequests;
private final List<String> pendingRequests;
private final List<String> pendingReadRequests;
private final Queue<LwM2mQueuedRequest> queuedRequests;
private boolean init;
private volatile boolean updateFw;
public Object clone() throws CloneNotSupportedException {
return super.clone();
@ -75,12 +77,14 @@ public class LwM2mClient implements Cloneable {
this.securityInfo = securityInfo;
this.credentialsResponse = credentialsResponse;
this.delayedRequests = new ConcurrentHashMap<>();
this.pendingRequests = new CopyOnWriteArrayList<>();
this.pendingReadRequests = new CopyOnWriteArrayList<>();
this.resources = new ConcurrentHashMap<>();
this.profileId = profileId;
this.sessionId = sessionId;
this.init = false;
this.updateFw = false;
this.queuedRequests = new ConcurrentLinkedQueue<>();
this.frUpdate = new LwM2mFirmwareUpdate();
}
public boolean saveResourceValue(String pathRez, LwM2mResource rez, LwM2mModelProvider modelProvider) {
@ -103,15 +107,13 @@ public class LwM2mClient implements Cloneable {
LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRez));
String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId());
String verRez = getVerFromPathIdVerOrId(pathRez);
return (verRez == null || verSupportedObject.equals(verRez)) ? modelProvider.getObjectModel(registration)
return verRez == null || verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration)
.getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()) : null;
}
public Collection<LwM2mResource> getNewResourcesForInstance(String pathRezIdVer, LwM2mModelProvider modelProvider,
LwM2mValueConverterImpl converter) {
LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRezIdVer));
String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId());
String verRez = getVerFromPathIdVerOrId(pathRezIdVer);
Collection<LwM2mResource> resources = ConcurrentHashMap.newKeySet();
Map<Integer, ResourceModel> resourceModels = modelProvider.getObjectModel(registration)
.getObjectModel(pathIds.getObjectId()).resources;
@ -170,11 +172,11 @@ public class LwM2mClient implements Cloneable {
.collect(Collectors.toSet());
}
public void initValue(LwM2mTransportServiceImpl serviceImpl, String path) {
public void initReadValue(DefaultLwM2MTransportMsgHandler serviceImpl, String path) {
if (path != null) {
this.pendingRequests.remove(path);
this.pendingReadRequests.remove(path);
}
if (this.pendingRequests.size() == 0) {
if (this.pendingReadRequests.size() == 0) {
this.init = true;
serviceImpl.putDelayedUpdateResourcesThingsboard(this);
}

18
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java

@ -25,7 +25,7 @@ import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil;
import java.util.Arrays;
import java.util.Map;
@ -34,7 +34,7 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromObjectIdToIdVer;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.convertPathFromObjectIdToIdVer;
@Service
@TbLwM2mTransportComponent
@ -82,12 +82,12 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
@Override
public LwM2mClient getLwM2mClientWithReg(Registration registration, String registrationId) {
LwM2mClient client = registrationId != null ?
LwM2mClient client = registrationId != null && this.lwM2mClients.containsKey(registrationId) ?
this.lwM2mClients.get(registrationId) :
this.lwM2mClients.containsKey(registration.getId()) ?
this.lwM2mClients.get(registration.getId()) :
this.lwM2mClients.get(registration.getEndpoint());
return client != null ? client : updateInSessionsLwM2MClient(registration);
registration !=null && this.lwM2mClients.containsKey(registration.getId()) ?
this.lwM2mClients.get(registration.getId()) : registration !=null && this.lwM2mClients.containsKey(registration) ?
this.lwM2mClients.get(registration.getEndpoint()) : null;
return client != null ? client : registration!= null ? updateInSessionsLwM2MClient(registration) : null;
}
@Override
@ -118,7 +118,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
*/
@Override
public LwM2mClient addLwM2mClientToSession(String identity) {
ReadResultSecurityStore store = lwM2MCredentialsSecurityInfoValidator.createAndValidateCredentialsSecurityInfo(identity, LwM2mTransportHandler.LwM2mTypeServer.CLIENT);
ReadResultSecurityStore store = lwM2MCredentialsSecurityInfoValidator.createAndValidateCredentialsSecurityInfo(identity, LwM2mTransportHandlerUtil.LwM2mTypeServer.CLIENT);
if (store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) {
UUID profileUuid = (store.getDeviceProfile() != null && addUpdateProfileParameters(store.getDeviceProfile())) ? store.getDeviceProfile().getUuidId() : null;
LwM2mClient client;
@ -165,7 +165,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
@Override
public boolean addUpdateProfileParameters(DeviceProfile deviceProfile) {
LwM2mClientProfile lwM2MClientProfile = LwM2mTransportHandler.getLwM2MClientProfileFromThingsboard(deviceProfile);
LwM2mClientProfile lwM2MClientProfile = LwM2mTransportHandlerUtil.getLwM2MClientProfileFromThingsboard(deviceProfile);
if (lwM2MClientProfile != null) {
profiles.put(deviceProfile.getUuidId(), lwM2MClientProfile);
return true;

27
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFirmwareUpdate.java

@ -0,0 +1,27 @@
/**
* Copyright © 2016-2021 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.transport.lwm2m.server.client;
import lombok.Data;
import java.util.UUID;
@Data
public class LwM2mFirmwareUpdate {
private volatile String clientFwVersion;
private volatile String currentFwVersion;
private volatile UUID currentFwId;
}

4
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java

@ -21,11 +21,11 @@ import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.server.registration.Registration;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper;
import java.util.concurrent.ConcurrentHashMap;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.validPathIdVer;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.validPathIdVer;
@Data
public class Lwm2mClientRpcRequest {

0
common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh

0
common/transport/lwm2m/src/main/resources/credentials/serverKeyStore.jks → common/transport/lwm2m/src/main/resources/lwm2mserver.jks

88
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java

@ -47,6 +47,7 @@ import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.msg.EncryptionUtil;
import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
@ -59,6 +60,7 @@ import org.thingsboard.server.common.transport.auth.TransportDeviceInfo;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.common.transport.service.DefaultTransportService;
import org.thingsboard.server.common.transport.service.SessionMetaData;
import org.thingsboard.server.common.transport.util.SslUtil;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
@ -68,7 +70,6 @@ import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx;
import org.thingsboard.server.transport.mqtt.session.GatewaySessionHandler;
import org.thingsboard.server.transport.mqtt.session.MqttTopicMatcher;
import org.thingsboard.server.common.transport.util.SslUtil;
import javax.net.ssl.SSLPeerUnverifiedException;
import java.io.IOException;
@ -97,6 +98,8 @@ import static io.netty.handler.codec.mqtt.MqttMessageType.UNSUBACK;
import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE;
import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE;
import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_FIRMWARE_REQUEST_TOPIC_PATTERN;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_SOFTWARE_REQUEST_TOPIC_PATTERN;
/**
* @author Andrew Shvayka
@ -104,7 +107,9 @@ import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE;
@Slf4j
public class MqttTransportHandler extends ChannelInboundHandlerAdapter implements GenericFutureListener<Future<? super Void>>, SessionMsgListener {
private static final Pattern FW_PATTERN = Pattern.compile("v2/fw/request/(?<requestId>\\d+)/chunk/(?<chunk>\\d+)");
private static final Pattern FW_REQUEST_PATTERN = Pattern.compile(DEVICE_FIRMWARE_REQUEST_TOPIC_PATTERN);
private static final Pattern SW_REQUEST_PATTERN = Pattern.compile(DEVICE_SOFTWARE_REQUEST_TOPIC_PATTERN);
private static final String PAYLOAD_TOO_LARGE = "PAYLOAD_TOO_LARGE";
@ -314,38 +319,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
} else if (topicName.equals(MqttTopics.DEVICE_CLAIM_TOPIC)) {
TransportProtos.ClaimDeviceMsg claimDeviceMsg = payloadAdaptor.convertToClaimDevice(deviceSessionCtx, mqttMsg);
transportService.process(deviceSessionCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(ctx, msgId, claimDeviceMsg));
} else if ((fwMatcher = FW_PATTERN.matcher(topicName)).find()) {
String payload = mqttMsg.content().toString(UTF8);
int chunkSize = StringUtils.isNotEmpty(payload) ? Integer.parseInt(payload) : 0;
String requestId = fwMatcher.group("requestId");
int chunk = Integer.parseInt(fwMatcher.group("chunk"));
if (chunkSize > 0) {
this.fwChunkSizes.put(requestId, chunkSize);
} else {
chunkSize = fwChunkSizes.getOrDefault(requestId, 0);
}
if (chunkSize > context.getMaxPayloadSize()) {
sendFirmwareError(ctx, PAYLOAD_TOO_LARGE);
return;
}
String firmwareId = fwSessions.get(requestId);
if (firmwareId != null) {
sendFirmware(ctx, mqttMsg.variableHeader().packetId(), firmwareId, requestId, chunkSize, chunk);
} else {
TransportProtos.SessionInfoProto sessionInfo = deviceSessionCtx.getSessionInfo();
TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
.setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
.setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
.setTenantIdMSB(sessionInfo.getTenantIdMSB())
.setTenantIdLSB(sessionInfo.getTenantIdLSB())
.build();
transportService.process(deviceSessionCtx.getSessionInfo(), getFirmwareRequestMsg,
new FirmwareCallback(ctx, mqttMsg.variableHeader().packetId(), getFirmwareRequestMsg, requestId, chunkSize, chunk));
}
} else if ((fwMatcher = FW_REQUEST_PATTERN.matcher(topicName)).find()) {
getFirmwareCallback(ctx, mqttMsg, msgId, fwMatcher, FirmwareType.FIRMWARE);
} else if ((fwMatcher = SW_REQUEST_PATTERN.matcher(topicName)).find()) {
getFirmwareCallback(ctx, mqttMsg, msgId, fwMatcher, FirmwareType.SOFTWARE);
} else {
transportService.reportActivity(deviceSessionCtx.getSessionInfo());
ack(ctx, msgId);
@ -357,6 +334,41 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
}
}
private void getFirmwareCallback(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, int msgId, Matcher fwMatcher, FirmwareType type) {
String payload = mqttMsg.content().toString(UTF8);
int chunkSize = StringUtils.isNotEmpty(payload) ? Integer.parseInt(payload) : 0;
String requestId = fwMatcher.group("requestId");
int chunk = Integer.parseInt(fwMatcher.group("chunk"));
if (chunkSize > 0) {
this.fwChunkSizes.put(requestId, chunkSize);
} else {
chunkSize = fwChunkSizes.getOrDefault(requestId, 0);
}
if (chunkSize > context.getMaxPayloadSize()) {
sendFirmwareError(ctx, PAYLOAD_TOO_LARGE);
return;
}
String firmwareId = fwSessions.get(requestId);
if (firmwareId != null) {
sendFirmware(ctx, mqttMsg.variableHeader().packetId(), firmwareId, requestId, chunkSize, chunk, type);
} else {
TransportProtos.SessionInfoProto sessionInfo = deviceSessionCtx.getSessionInfo();
TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
.setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
.setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
.setTenantIdMSB(sessionInfo.getTenantIdMSB())
.setTenantIdLSB(sessionInfo.getTenantIdLSB())
.setType(type.name())
.build();
transportService.process(deviceSessionCtx.getSessionInfo(), getFirmwareRequestMsg,
new FirmwareCallback(ctx, msgId, getFirmwareRequestMsg, requestId, chunkSize, chunk));
}
}
private void ack(ChannelHandlerContext ctx, int msgId) {
if (msgId > 0) {
ctx.writeAndFlush(createMqttPubAckMsg(msgId));
@ -435,7 +447,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) {
FirmwareId firmwareId = new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB()));
fwSessions.put(requestId, firmwareId.toString());
sendFirmware(ctx, msgId, firmwareId.toString(), requestId, chunkSize, chunk);
sendFirmware(ctx, msgId, firmwareId.toString(), requestId, chunkSize, chunk, FirmwareType.valueOf(response.getType()));
} else {
sendFirmwareError(ctx, response.getResponseStatus().toString());
}
@ -448,13 +460,13 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
}
}
private void sendFirmware(ChannelHandlerContext ctx, int msgId, String firmwareId, String requestId, int chunkSize, int chunk) {
private void sendFirmware(ChannelHandlerContext ctx, int msgId, String firmwareId, String requestId, int chunkSize, int chunk, FirmwareType type) {
log.trace("[{}] Send firmware [{}] to device!", sessionId, firmwareId);
ack(ctx, msgId);
try {
byte[] firmwareChunk = context.getFirmwareDataCache().get(firmwareId, chunkSize, chunk);
deviceSessionCtx.getPayloadAdaptor()
.convertToPublish(deviceSessionCtx, firmwareChunk, requestId, chunk)
.convertToPublish(deviceSessionCtx, firmwareChunk, requestId, chunk, type)
.ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
if (firmwareChunk != null && chunkSize != firmwareChunk.length) {
scheduler.schedule(() -> processDisconnect(ctx), 60, TimeUnit.SECONDS);
@ -504,6 +516,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
case MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC:
case MqttTopics.DEVICE_FIRMWARE_RESPONSES_TOPIC:
case MqttTopics.DEVICE_FIRMWARE_ERROR_TOPIC:
case MqttTopics.DEVICE_SOFTWARE_RESPONSES_TOPIC:
case MqttTopics.DEVICE_SOFTWARE_ERROR_TOPIC:
registerSubQoS(topic, grantedQoSList, reqQoS);
break;
default:

8
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java

@ -30,6 +30,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.gen.transport.TransportProtos;
@ -43,6 +44,9 @@ import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT;
/**
* @author Andrew Shvayka
*/
@ -151,8 +155,8 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor {
}
@Override
public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk) {
return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX + requestId + "/chunk/" + chunk, firmwareChunk));
public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) {
return Optional.of(createMqttPublishMsg(ctx, String.format(DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT, firmwareType.getKeyPrefix(), requestId, chunk), firmwareChunk));
}
public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException {

3
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java

@ -23,6 +23,7 @@ import io.netty.handler.codec.mqtt.MqttMessage;
import io.netty.handler.codec.mqtt.MqttMessageType;
import io.netty.handler.codec.mqtt.MqttPublishMessage;
import io.netty.handler.codec.mqtt.MqttPublishVariableHeader;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
@ -77,7 +78,7 @@ public interface MqttTransportAdaptor {
Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, ProvisionDeviceResponseMsg provisionResponse) throws AdaptorException;
Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk) throws AdaptorException;
Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) throws AdaptorException;
default MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, byte[] payloadInBytes) {
MqttFixedHeader mqttFixedHeader =

7
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/ProtoMqttAdaptor.java

@ -28,6 +28,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.common.transport.adaptor.ProtoConverter;
@ -38,6 +39,8 @@ import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionConte
import java.util.Optional;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT;
@Component
@Slf4j
public class ProtoMqttAdaptor implements MqttTransportAdaptor {
@ -165,8 +168,8 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor {
}
@Override
public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk) throws AdaptorException {
return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX + requestId + "/" + chunk, firmwareChunk));
public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) throws AdaptorException {
return Optional.of(createMqttPublishMsg(ctx, String.format(DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT, firmwareType.getKeyPrefix(), requestId, chunk), firmwareChunk));
}
@Override

4
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java

@ -51,11 +51,13 @@ public abstract class TransportContext {
@Getter
private ExecutorService executor;
@Getter
@Autowired
private FirmwareDataCache firmwareDataCache;
@Autowired
private TransportResourceCache transportResourceCache;
@PostConstruct
public void init() {
executor = ThingsBoardExecutors.newWorkStealingPool(50, getClass());

217
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java

@ -1,217 +0,0 @@
/**
* Copyright © 2016-2021 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.common.transport.lwm2m;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.server.model.LwM2mModelProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
@Slf4j
@Component
@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true') || '${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'")
public class LwM2MTransportConfigServer {
@Getter
private String KEY_STORE_DEFAULT_RESOURCE_PATH = "credentials";
@Getter
private String KEY_STORE_DEFAULT_FILE = "serverKeyStore.jks";
@Getter
private String APP_DIR = "common";
@Getter
private String TRANSPORT_DIR = "transport";
@Getter
private String LWM2M_DIR = "lwm2m";
@Getter
private String SRC_DIR = "src";
@Getter
private String MAIN_DIR = "main";
@Getter
private String RESOURCES_DIR = "resources";
@Getter
private String BASE_DIR_PATH = System.getProperty("user.dir");
@Getter
// private String PATH_DATA_MICROSERVICE = "/usr/share/tb-lwm2m-transport/data$";
private String PATH_DATA = "data";
@Getter
@Setter
private LwM2mModelProvider modelProvider;
@Getter
@Value("${transport.lwm2m.timeout:}")
private Long timeout;
@Getter
@Value("${transport.sessions.report_timeout}")
private long sessionReportTimeout;
@Getter
@Value("${transport.lwm2m.recommended_ciphers:}")
private boolean recommendedCiphers;
@Getter
@Value("${transport.lwm2m.recommended_supported_groups:}")
private boolean recommendedSupportedGroups;
@Getter
@Value("${transport.lwm2m.response_pool_size:}")
private int responsePoolSize;
@Getter
@Value("${transport.lwm2m.registered_pool_size:}")
private int registeredPoolSize;
@Getter
@Value("${transport.lwm2m.update_registered_pool_size:}")
private int updateRegisteredPoolSize;
@Getter
@Value("${transport.lwm2m.un_registered_pool_size:}")
private int unRegisteredPoolSize;
@Getter
@Value("${transport.lwm2m.secure.key_store_type:}")
private String keyStoreType;
@Getter
@Value("${transport.lwm2m.secure.key_store_path_file:}")
private String keyStorePathFile;
@Getter
@Setter
private KeyStore keyStoreValue;
@Getter
@Value("${transport.lwm2m.secure.key_store_password:}")
private String keyStorePasswordServer;
@Getter
@Value("${transport.lwm2m.secure.root_alias:}")
private String rootAlias;
@Getter
@Value("${transport.lwm2m.secure.enable_gen_new_key_psk_rpk:}")
private Boolean enableGenNewKeyPskRpk;
@Getter
@Value("${transport.lwm2m.server.id:}")
private Integer serverId;
@Getter
@Value("${transport.lwm2m.server.bind_address:}")
private String serverHost;
@Getter
@Value("${transport.lwm2m.server.secure.bind_address_security:}")
private String serverHostSecurity;
@Getter
@Value("${transport.lwm2m.server.bind_port_no_sec:}")
private Integer serverPortNoSec;
@Getter
@Value("${transport.lwm2m.server.secure.bind_port_security:}")
private Integer serverPortSecurity;
@Getter
@Value("${transport.lwm2m.server.secure.public_x:}")
private String serverPublicX;
@Getter
@Value("${transport.lwm2m.server.secure.public_y:}")
private String serverPublicY;
@Getter
@Value("${transport.lwm2m.server.secure.private_encoded:}")
private String serverPrivateEncoded;
@Getter
@Value("${transport.lwm2m.server.secure.alias:}")
private String serverAlias;
@PostConstruct
public void init() {
this.getInKeyStore();
}
private KeyStore getInKeyStore() {
try {
if (keyStoreValue != null && keyStoreValue.size() > 0)
return keyStoreValue;
} catch (KeyStoreException e) {
log.error("Uninitialized keystore [{}]", keyStoreValue.toString());
}
Path keyStorePath = (keyStorePathFile != null && !keyStorePathFile.isEmpty()) ? Paths.get(keyStorePathFile) :
(new File(Paths.get(getBaseDirPath(), PATH_DATA, KEY_STORE_DEFAULT_RESOURCE_PATH, KEY_STORE_DEFAULT_FILE).toUri()).isFile()) ?
Paths.get(getBaseDirPath(), PATH_DATA, KEY_STORE_DEFAULT_RESOURCE_PATH, KEY_STORE_DEFAULT_FILE) :
Paths.get(getBaseDirPath(), APP_DIR, TRANSPORT_DIR, LWM2M_DIR, SRC_DIR, MAIN_DIR, RESOURCES_DIR, KEY_STORE_DEFAULT_RESOURCE_PATH, KEY_STORE_DEFAULT_FILE);
File keyStoreFile = new File(keyStorePath.toUri());
if (keyStoreFile.isFile()) {
try {
InputStream inKeyStore = new FileInputStream(keyStoreFile);
keyStoreValue = KeyStore.getInstance(keyStoreType);
keyStoreValue.load(inKeyStore, keyStorePasswordServer == null ? null : keyStorePasswordServer.toCharArray());
} catch (CertificateException | NoSuchAlgorithmException | IOException | KeyStoreException e) {
log.error("[{}] Unable to load KeyStore files server, folder is not a directory", e.getMessage());
keyStoreValue = null;
}
log.info("[{}] Load KeyStore files server, folder is a directory", keyStoreFile.getAbsoluteFile());
} else {
log.error("[{}] Unable to load KeyStore files server, is not a file", keyStoreFile.getAbsoluteFile());
keyStoreValue = null;
}
return keyStoreValue;
}
private String getBaseDirPath() {
Path FULL_FILE_PATH;
if (BASE_DIR_PATH.endsWith("bin")) {
FULL_FILE_PATH = Paths.get(BASE_DIR_PATH.replaceAll("bin$", ""));
} else if (BASE_DIR_PATH.endsWith("conf")) {
FULL_FILE_PATH = Paths.get(BASE_DIR_PATH.replaceAll("conf$", ""));
} else if (BASE_DIR_PATH.endsWith("application")) {
FULL_FILE_PATH = Paths.get(BASE_DIR_PATH.substring(0, BASE_DIR_PATH.length() - "application".length()));
} else {
FULL_FILE_PATH = Paths.get(BASE_DIR_PATH);
}
return FULL_FILE_PATH.toUri().getPath();
}
}

2
dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java

@ -83,6 +83,8 @@ public interface DeviceDao extends Dao<Device>, TenantEntityDao {
PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(UUID tenantId, String type, PageLink pageLink);
PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(UUID tenantId, String type, PageLink pageLink);
/**
* Find device infos by tenantId, type and page link.
*

23
dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java

@ -56,6 +56,7 @@ import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfilePr
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.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
@ -406,9 +407,31 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
if (firmware == null) {
throw new DataValidationException("Can't assign non-existent firmware!");
}
if (!firmware.getType().equals(FirmwareType.FIRMWARE)) {
throw new DataValidationException("Can't assign firmware with type: " + firmware.getType());
}
if (firmware.getData() == null) {
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) {
Firmware software = firmwareService.findFirmwareById(tenantId, deviceProfile.getSoftwareId());
if (software == null) {
throw new DataValidationException("Can't assign non-existent software!");
}
if (!software.getType().equals(FirmwareType.SOFTWARE)) {
throw new DataValidationException("Can't assign software with type: " + software.getType());
}
if (software.getData() == null) {
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!");
}
}
}

34
dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java

@ -53,6 +53,7 @@ import org.thingsboard.server.common.data.device.data.Lwm2mDeviceTransportConfig
import org.thingsboard.server.common.data.device.data.MqttDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
@ -361,13 +362,22 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
@Override
public PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink) {
log.trace("Executing findDevicesByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
log.trace("Executing findDevicesByTenantIdAndTypeAndEmptyFirmware, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateString(type, "Incorrect type " + type);
validatePageLink(pageLink);
return deviceDao.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId.getId(), type, pageLink);
}
@Override
public PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(TenantId tenantId, String type, PageLink pageLink) {
log.trace("Executing findDevicesByTenantIdAndTypeAndEmptySoftware, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateString(type, "Incorrect type " + type);
validatePageLink(pageLink);
return deviceDao.findDevicesByTenantIdAndTypeAndEmptySoftware(tenantId.getId(), type, pageLink);
}
@Override
public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) {
log.trace("Executing findDeviceInfosByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
@ -696,9 +706,31 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
if (firmware == null) {
throw new DataValidationException("Can't assign non-existent firmware!");
}
if (!firmware.getType().equals(FirmwareType.FIRMWARE)) {
throw new DataValidationException("Can't assign firmware with type: " + firmware.getType());
}
if (firmware.getData() == null) {
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) {
Firmware software = firmwareService.findFirmwareById(tenantId, device.getSoftwareId());
if (software == null) {
throw new DataValidationException("Can't assign non-existent software!");
}
if (!software.getType().equals(FirmwareType.SOFTWARE)) {
throw new DataValidationException("Can't assign software with type: " + software.getType());
}
if (software.getData() == null) {
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!");
}
}
}
};

96
dao/src/main/java/org/thingsboard/server/dao/firmware/BaseFirmwareService.java

@ -27,13 +27,17 @@ import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.firmware.FirmwareDataCache;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.Firmware;
import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.FirmwareId;
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.dao.device.DeviceProfileDao;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
@ -56,6 +60,7 @@ public class BaseFirmwareService implements FirmwareService {
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
private final TenantDao tenantDao;
private final DeviceProfileDao deviceProfileDao;
private final FirmwareDao firmwareDao;
private final FirmwareInfoDao firmwareInfoDao;
private final CacheManager cacheManager;
@ -124,7 +129,8 @@ public class BaseFirmwareService implements FirmwareService {
public ListenableFuture<FirmwareInfo> findFirmwareInfoByIdAsync(TenantId tenantId, FirmwareId firmwareId) {
log.trace("Executing findFirmwareInfoByIdAsync [{}]", firmwareId);
validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId);
return firmwareInfoDao.findByIdAsync(tenantId, firmwareId.getId()); }
return firmwareInfoDao.findByIdAsync(tenantId, firmwareId.getId());
}
@Override
public PageData<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink) {
@ -135,11 +141,11 @@ public class BaseFirmwareService implements FirmwareService {
}
@Override
public PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink) {
public PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink) {
log.trace("Executing findTenantFirmwaresByTenantIdAndHasData, tenantId [{}], hasData [{}] pageLink [{}]", tenantId, hasData, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validatePageLink(pageLink);
return firmwareInfoDao.findFirmwareInfoByTenantIdAndHasData(tenantId, hasData, pageLink);
return firmwareInfoDao.findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, firmwareType, hasData, pageLink);
}
@Override
@ -157,6 +163,10 @@ public class BaseFirmwareService implements FirmwareService {
throw new DataValidationException("The firmware referenced by the devices cannot be deleted!");
} else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_firmware_device_profile")) {
throw new DataValidationException("The firmware referenced by the device profile cannot be deleted!");
} else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_software_device")) {
throw new DataValidationException("The software referenced by the devices cannot be deleted!");
} else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_software_device_profile")) {
throw new DataValidationException("The software referenced by the device profile cannot be deleted!");
} else {
throw t;
}
@ -173,29 +183,15 @@ public class BaseFirmwareService implements FirmwareService {
private DataValidator<FirmwareInfo> firmwareInfoValidator = new DataValidator<>() {
@Override
protected void validateDataImpl(TenantId tenantId, FirmwareInfo firmware) {
if (firmware.getTenantId() == null) {
throw new DataValidationException("Firmware should be assigned to tenant!");
} else {
Tenant tenant = tenantDao.findById(firmware.getTenantId(), firmware.getTenantId().getId());
if (tenant == null) {
throw new DataValidationException("Firmware is referencing to non-existent tenant!");
}
}
if (StringUtils.isEmpty(firmware.getTitle())) {
throw new DataValidationException("Firmware title should be specified!");
}
if (StringUtils.isEmpty(firmware.getVersion())) {
throw new DataValidationException("Firmware version should be specified!");
}
protected void validateDataImpl(TenantId tenantId, FirmwareInfo firmwareInfo) {
validateImpl(firmwareInfo);
}
@Override
protected void validateUpdate(TenantId tenantId, FirmwareInfo firmware) {
FirmwareInfo firmwareOld = firmwareInfoDao.findById(tenantId, firmware.getUuidId());
validateUpdateDeviceProfile(firmware, firmwareOld);
BaseFirmwareService.validateUpdate(firmware, firmwareOld);
}
};
@ -204,22 +200,7 @@ public class BaseFirmwareService implements FirmwareService {
@Override
protected void validateDataImpl(TenantId tenantId, Firmware firmware) {
if (firmware.getTenantId() == null) {
throw new DataValidationException("Firmware should be assigned to tenant!");
} else {
Tenant tenant = tenantDao.findById(firmware.getTenantId(), firmware.getTenantId().getId());
if (tenant == null) {
throw new DataValidationException("Firmware is referencing to non-existent tenant!");
}
}
if (StringUtils.isEmpty(firmware.getTitle())) {
throw new DataValidationException("Firmware title should be specified!");
}
if (StringUtils.isEmpty(firmware.getVersion())) {
throw new DataValidationException("Firmware version should be specified!");
}
validateImpl(firmware);
if (StringUtils.isEmpty(firmware.getFileName())) {
throw new DataValidationException("Firmware file name should be specified!");
@ -267,6 +248,7 @@ public class BaseFirmwareService implements FirmwareService {
protected void validateUpdate(TenantId tenantId, Firmware firmware) {
Firmware firmwareOld = firmwareDao.findById(tenantId, firmware.getUuidId());
validateUpdateDeviceProfile(firmware, firmwareOld);
BaseFirmwareService.validateUpdate(firmware, firmwareOld);
if (firmwareOld.getData() != null && !firmwareOld.getData().equals(firmware.getData())) {
@ -275,7 +257,19 @@ public class BaseFirmwareService implements FirmwareService {
}
};
private void validateUpdateDeviceProfile(FirmwareInfo firmware, FirmwareInfo firmwareOld) {
if (firmwareOld.getDeviceProfileId() != null && !firmwareOld.getDeviceProfileId().equals(firmware.getDeviceProfileId())) {
if (firmwareInfoDao.isFirmwareUsed(firmwareOld.getId(), firmware.getType(), firmwareOld.getDeviceProfileId())) {
throw new DataValidationException("Can`t update deviceProfileId because firmware is already in use!");
}
}
}
private static void validateUpdate(FirmwareInfo firmware, FirmwareInfo firmwareOld) {
if (!firmwareOld.getType().equals(firmware.getType())) {
throw new DataValidationException("Updating type is prohibited!");
}
if (!firmwareOld.getTitle().equals(firmware.getTitle())) {
throw new DataValidationException("Updating firmware title is prohibited!");
}
@ -305,6 +299,36 @@ public class BaseFirmwareService implements FirmwareService {
}
}
private void validateImpl(FirmwareInfo firmwareInfo) {
if (firmwareInfo.getTenantId() == null) {
throw new DataValidationException("Firmware should be assigned to tenant!");
} else {
Tenant tenant = tenantDao.findById(firmwareInfo.getTenantId(), firmwareInfo.getTenantId().getId());
if (tenant == null) {
throw new DataValidationException("Firmware is referencing to non-existent tenant!");
}
}
if (firmwareInfo.getDeviceProfileId() != null) {
DeviceProfile deviceProfile = deviceProfileDao.findById(firmwareInfo.getTenantId(), firmwareInfo.getDeviceProfileId().getId());
if (deviceProfile == null) {
throw new DataValidationException("Firmware is referencing to non-existent device profile!");
}
}
if (firmwareInfo.getType() == null) {
throw new DataValidationException("Type should be specified!");
}
if (StringUtils.isEmpty(firmwareInfo.getTitle())) {
throw new DataValidationException("Firmware title should be specified!");
}
if (StringUtils.isEmpty(firmwareInfo.getVersion())) {
throw new DataValidationException("Firmware version should be specified!");
}
}
private PaginatedRemover<TenantId, FirmwareInfo> tenantFirmwareRemover =
new PaginatedRemover<>() {

7
dao/src/main/java/org/thingsboard/server/dao/firmware/FirmwareInfoDao.java

@ -16,6 +16,9 @@
package org.thingsboard.server.dao.firmware;
import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
@ -27,6 +30,8 @@ public interface FirmwareInfoDao extends Dao<FirmwareInfo> {
PageData<FirmwareInfo> findFirmwareInfoByTenantId(TenantId tenantId, PageLink pageLink);
PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink);
PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink);
boolean isFirmwareUsed(FirmwareId firmwareId, FirmwareType type, DeviceProfileId deviceProfileId);
}

4
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -154,6 +154,7 @@ public class ModelConstants {
public static final String DEVICE_DEVICE_PROFILE_ID_PROPERTY = "device_profile_id";
public static final String DEVICE_DEVICE_DATA_PROPERTY = "device_data";
public static final String DEVICE_FIRMWARE_ID_PROPERTY = "firmware_id";
public static final String DEVICE_SOFTWARE_ID_PROPERTY = "software_id";
public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text";
public static final String DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_by_type_and_search_text";
@ -178,6 +179,7 @@ public class ModelConstants {
public static final String DEVICE_PROFILE_DEFAULT_QUEUE_NAME_PROPERTY = "default_queue_name";
public static final String DEVICE_PROFILE_PROVISION_DEVICE_KEY = "provision_device_key";
public static final String DEVICE_PROFILE_FIRMWARE_ID_PROPERTY = "firmware_id";
public static final String DEVICE_PROFILE_SOFTWARE_ID_PROPERTY = "software_id";
/**
* Cassandra entityView constants.
@ -476,6 +478,8 @@ public class ModelConstants {
*/
public static final String FIRMWARE_TABLE_NAME = "firmware";
public static final String FIRMWARE_TENANT_ID_COLUMN = TENANT_ID_COLUMN;
public static final String FIRMWARE_DEVICE_PROFILE_ID_COLUMN = "device_profile_id";
public static final String FIRMWARE_TYPE_COLUMN = "type";
public static final String FIRMWARE_TITLE_COLUMN = TITLE_PROPERTY;
public static final String FIRMWARE_VERSION_COLUMN = "version";
public static final String FIRMWARE_FILE_NAME_COLUMN = "file_name";

16
dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java

@ -22,6 +22,7 @@ import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.device.data.DeviceData;
import org.thingsboard.server.common.data.id.CustomerId;
@ -32,7 +33,6 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.model.SearchTextEntity;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.dao.util.mapping.JsonBinaryType;
import org.thingsboard.server.dao.util.mapping.JsonStringType;
@ -43,8 +43,8 @@ import java.util.UUID;
@Data
@EqualsAndHashCode(callSuper = true)
@TypeDefs({
@TypeDef(name = "json", typeClass = JsonStringType.class),
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
@TypeDef(name = "json", typeClass = JsonStringType.class),
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
})
@MappedSuperclass
public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEntity<T> implements SearchTextEntity<T> {
@ -77,6 +77,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
@Column(name = ModelConstants.DEVICE_FIRMWARE_ID_PROPERTY, columnDefinition = "uuid")
private UUID firmwareId;
@Column(name = ModelConstants.DEVICE_SOFTWARE_ID_PROPERTY, columnDefinition = "uuid")
private UUID softwareId;
@Type(type = "jsonb")
@Column(name = ModelConstants.DEVICE_DEVICE_DATA_PROPERTY, columnDefinition = "jsonb")
private JsonNode deviceData;
@ -102,6 +105,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
if (device.getFirmwareId() != null) {
this.firmwareId = device.getFirmwareId().getId();
}
if (device.getSoftwareId() != null) {
this.softwareId = device.getSoftwareId().getId();
}
this.deviceData = JacksonUtil.convertValue(device.getDeviceData(), ObjectNode.class);
this.name = device.getName();
this.type = device.getType();
@ -122,6 +128,7 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
this.searchText = deviceEntity.getSearchText();
this.additionalInfo = deviceEntity.getAdditionalInfo();
this.firmwareId = deviceEntity.getFirmwareId();
this.softwareId = deviceEntity.getSoftwareId();
}
@Override
@ -149,6 +156,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
if (firmwareId != null) {
device.setFirmwareId(new FirmwareId(firmwareId));
}
if (softwareId != null) {
device.setSoftwareId(new FirmwareId(softwareId));
}
device.setDeviceData(JacksonUtil.convertValue(deviceData, DeviceData.class));
device.setName(name);
device.setType(type);

18
dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java

@ -21,9 +21,10 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileType;
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.device.profile.DeviceProfileData;
import org.thingsboard.server.common.data.id.DeviceProfileId;
@ -33,7 +34,6 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.model.SearchTextEntity;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.dao.util.mapping.JsonBinaryType;
import javax.persistence.Column;
@ -87,12 +87,15 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
@Column(name = ModelConstants.DEVICE_PROFILE_PROFILE_DATA_PROPERTY, columnDefinition = "jsonb")
private JsonNode profileData;
@Column(name=ModelConstants.DEVICE_PROFILE_PROVISION_DEVICE_KEY)
@Column(name = ModelConstants.DEVICE_PROFILE_PROVISION_DEVICE_KEY)
private String provisionDeviceKey;
@Column(name=ModelConstants.DEVICE_PROFILE_FIRMWARE_ID_PROPERTY)
@Column(name = ModelConstants.DEVICE_PROFILE_FIRMWARE_ID_PROPERTY)
private UUID firmwareId;
@Column(name = ModelConstants.DEVICE_PROFILE_SOFTWARE_ID_PROPERTY)
private UUID softwareId;
public DeviceProfileEntity() {
super();
}
@ -120,6 +123,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
if (deviceProfile.getFirmwareId() != null) {
this.firmwareId = deviceProfile.getFirmwareId().getId();
}
if (deviceProfile.getSoftwareId() != null) {
this.firmwareId = deviceProfile.getSoftwareId().getId();
}
}
@Override
@ -160,6 +166,10 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
deviceProfile.setFirmwareId(new FirmwareId(firmwareId));
}
if (softwareId != null) {
deviceProfile.setSoftwareId(new FirmwareId(softwareId));
}
return deviceProfile;
}
}

21
dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareEntity.java

@ -21,6 +21,8 @@ import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.thingsboard.server.common.data.Firmware;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity;
@ -30,6 +32,8 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Lob;
import javax.persistence.Table;
import java.nio.ByteBuffer;
@ -40,10 +44,12 @@ import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DEVICE_PROFILE_ID_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TYPE_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY;
@ -57,6 +63,13 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex
@Column(name = FIRMWARE_TENANT_ID_COLUMN)
private UUID tenantId;
@Column(name = FIRMWARE_DEVICE_PROFILE_ID_COLUMN)
private UUID deviceProfileId;
@Enumerated(EnumType.STRING)
@Column(name = FIRMWARE_TYPE_COLUMN)
private FirmwareType type;
@Column(name = FIRMWARE_TITLE_COLUMN)
private String title;
@ -97,6 +110,10 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex
this.createdTime = firmware.getCreatedTime();
this.setUuid(firmware.getUuidId());
this.tenantId = firmware.getTenantId().getId();
if (firmware.getDeviceProfileId() != null) {
this.deviceProfileId = firmware.getDeviceProfileId().getId();
}
this.type = firmware.getType();
this.title = firmware.getTitle();
this.version = firmware.getVersion();
this.fileName = firmware.getFileName();
@ -123,6 +140,10 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex
Firmware firmware = new Firmware(new FirmwareId(id));
firmware.setCreatedTime(createdTime);
firmware.setTenantId(new TenantId(tenantId));
if (deviceProfileId != null) {
firmware.setDeviceProfileId(new DeviceProfileId(deviceProfileId));
}
firmware.setType(type);
firmware.setTitle(title);
firmware.setVersion(version);
firmware.setFileName(fileName);

27
dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareInfoEntity.java

@ -22,6 +22,8 @@ import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity;
@ -31,6 +33,8 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.util.UUID;
@ -38,13 +42,13 @@ import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_ALGORITHM_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DEVICE_PROFILE_ID_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_HAS_DATA_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TYPE_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY;
@ -58,6 +62,13 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
@Column(name = FIRMWARE_TENANT_ID_COLUMN)
private UUID tenantId;
@Column(name = FIRMWARE_DEVICE_PROFILE_ID_COLUMN)
private UUID deviceProfileId;
@Enumerated(EnumType.STRING)
@Column(name = FIRMWARE_TYPE_COLUMN)
private FirmwareType type;
@Column(name = FIRMWARE_TITLE_COLUMN)
private String title;
@ -97,6 +108,10 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
this.createdTime = firmware.getCreatedTime();
this.setUuid(firmware.getUuidId());
this.tenantId = firmware.getTenantId().getId();
this.type = firmware.getType();
if (firmware.getDeviceProfileId() != null) {
this.deviceProfileId = firmware.getDeviceProfileId().getId();
}
this.title = firmware.getTitle();
this.version = firmware.getVersion();
this.fileName = firmware.getFileName();
@ -107,12 +122,14 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
this.additionalInfo = firmware.getAdditionalInfo();
}
public FirmwareInfoEntity(UUID id, long createdTime, UUID tenantId, String title, String version,
public FirmwareInfoEntity(UUID id, long createdTime, UUID tenantId, UUID deviceProfileId, FirmwareType type, String title, String version,
String fileName, String contentType, String checksumAlgorithm, String checksum, Long dataSize,
Object additionalInfo, boolean hasData) {
this.id = id;
this.createdTime = createdTime;
this.tenantId = tenantId;
this.deviceProfileId = deviceProfileId;
this.type = type;
this.title = title;
this.version = version;
this.fileName = fileName;
@ -139,6 +156,10 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
FirmwareInfo firmware = new FirmwareInfo(new FirmwareId(id));
firmware.setCreatedTime(createdTime);
firmware.setTenantId(new TenantId(tenantId));
if (deviceProfileId != null) {
firmware.setDeviceProfileId(new DeviceProfileId(deviceProfileId));
}
firmware.setType(type);
firmware.setTitle(title);
firmware.setVersion(version);
firmware.setFileName(fileName);

9
dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java

@ -104,6 +104,15 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit
@Param("textSearch") String textSearch,
Pageable pageable);
@Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " +
"AND d.type = :type " +
"AND d.softwareId = null " +
"AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))")
Page<DeviceEntity> findByTenantIdAndTypeAndSoftwareIdIsNull(@Param("tenantId") UUID tenantId,
@Param("type") String type,
@Param("textSearch") String textSearch,
Pageable pageable);
@Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " +
"FROM DeviceEntity d " +
"LEFT JOIN CustomerEntity c on c.id = d.customerId " +

10
dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java

@ -164,6 +164,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device>
DaoUtil.toPageable(pageLink)));
}
@Override
public PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(UUID tenantId, String type, PageLink pageLink) {
return DaoUtil.toPageData(
deviceRepository.findByTenantIdAndTypeAndSoftwareIdIsNull(
tenantId,
type,
Objects.toString(pageLink.getTextSearch(), ""),
DaoUtil.toPageable(pageLink)));
}
@Override
public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(UUID tenantId, String type, PageLink pageLink) {
return DaoUtil.toPageData(

28
dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareInfoRepository.java

@ -20,27 +20,41 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.dao.model.sql.FirmwareInfoEntity;
import java.util.UUID;
public interface FirmwareInfoRepository extends CrudRepository<FirmwareInfoEntity, UUID> {
@Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " +
@Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " +
"f.tenantId = :tenantId " +
"AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
Page<FirmwareInfoEntity> findAllByTenantId(@Param("tenantId") UUID tenantId,
@Param("searchText") String searchText,
Pageable pageable);
@Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " +
@Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " +
"f.tenantId = :tenantId " +
"AND f.deviceProfileId = :deviceProfileId " +
"AND f.type = :type " +
"AND ((f.data IS NOT NULL AND :hasData = true) OR (f.data IS NULL AND :hasData = false ))" +
"AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
Page<FirmwareInfoEntity> findAllByTenantIdAndHasData(@Param("tenantId") UUID tenantId,
@Param("hasData") boolean hasData,
@Param("searchText") String searchText,
Pageable pageable);
Page<FirmwareInfoEntity> findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData(@Param("tenantId") UUID tenantId,
@Param("deviceProfileId") UUID deviceProfileId,
@Param("type") FirmwareType type,
@Param("hasData") boolean hasData,
@Param("searchText") String searchText,
Pageable pageable);
@Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE f.id = :id")
@Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE f.id = :id")
FirmwareInfoEntity findFirmwareInfoById(@Param("id") UUID id);
@Query(value = "SELECT exists(SELECT * " +
"FROM device_profile AS dp " +
"LEFT JOIN device AS d ON dp.id = d.device_profile_id " +
"WHERE dp.id = :deviceProfileId AND " +
"(('FIRMWARE' = :type AND (dp.firmware_id = :firmwareId OR d.firmware_id = :firmwareId)) " +
"OR ('SOFTWARE' = :type AND (dp.software_id = :firmwareId or d.software_id = :firmwareId))))", nativeQuery = true)
boolean isFirmwareUsed(@Param("firmwareId") UUID firmwareId, @Param("deviceProfileId") UUID deviceProfileId, @Param("type") String type);
}

14
dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareInfoDao.java

@ -20,6 +20,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
@ -73,12 +76,19 @@ public class JpaFirmwareInfoDao extends JpaAbstractSearchTextDao<FirmwareInfoEnt
}
@Override
public PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink) {
public PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink) {
return DaoUtil.toPageData(firmwareInfoRepository
.findAllByTenantIdAndHasData(
.findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData(
tenantId.getId(),
deviceProfileId.getId(),
firmwareType,
hasData,
Objects.toString(pageLink.getTextSearch(), ""),
DaoUtil.toPageable(pageLink)));
}
@Override
public boolean isFirmwareUsed(FirmwareId firmwareId, FirmwareType type, DeviceProfileId deviceProfileId) {
return firmwareInfoRepository.isFirmwareUsed(firmwareId.getId(), deviceProfileId.getId(), type.name());
}
}

10
dao/src/main/resources/sql/schema-entities-hsql.sql

@ -162,6 +162,8 @@ CREATE TABLE IF NOT EXISTS firmware (
id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY,
created_time bigint NOT NULL,
tenant_id uuid NOT NULL,
device_profile_id uuid ,
type varchar(32) NOT NULL,
title varchar(255) NOT NULL,
version varchar(255) NOT NULL,
file_name varchar(255),
@ -188,13 +190,15 @@ CREATE TABLE IF NOT EXISTS device_profile (
is_default boolean,
tenant_id uuid,
firmware_id uuid,
software_id uuid,
default_rule_chain_id uuid,
default_queue_name varchar(255),
provision_device_key varchar,
CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name),
CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key),
CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id),
CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id)
CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id),
CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES firmware(id)
);
CREATE TABLE IF NOT EXISTS device (
@ -210,9 +214,11 @@ CREATE TABLE IF NOT EXISTS device (
search_text varchar(255),
tenant_id uuid,
firmware_id uuid,
software_id uuid,
CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name),
CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id),
CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id)
CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id),
CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES firmware(id)
);
CREATE TABLE IF NOT EXISTS device_credentials (

1
dao/src/main/resources/sql/schema-entities-idx.sql

@ -45,3 +45,4 @@ CREATE INDEX IF NOT EXISTS idx_asset_type ON asset(tenant_id, type);
CREATE INDEX IF NOT EXISTS idx_attribute_kv_by_key_and_last_update_ts ON attribute_kv(entity_id, attribute_key, last_update_ts desc);
CREATE INDEX IF NOT EXISTS idx_audit_log_tenant_id_and_created_time ON audit_log(tenant_id, created_time);

20
dao/src/main/resources/sql/schema-entities.sql

@ -180,6 +180,8 @@ CREATE TABLE IF NOT EXISTS firmware (
id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY,
created_time bigint NOT NULL,
tenant_id uuid NOT NULL,
device_profile_id uuid ,
type varchar(32) NOT NULL,
title varchar(255) NOT NULL,
version varchar(255) NOT NULL,
file_name varchar(255),
@ -191,6 +193,7 @@ CREATE TABLE IF NOT EXISTS firmware (
additional_info varchar,
search_text varchar(255),
CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version)
-- CONSTRAINT fk_device_profile_firmware FOREIGN KEY (device_profile_id) REFERENCES device_profile(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS device_profile (
@ -206,15 +209,26 @@ CREATE TABLE IF NOT EXISTS device_profile (
is_default boolean,
tenant_id uuid,
firmware_id uuid,
software_id uuid,
default_rule_chain_id uuid,
default_queue_name varchar(255),
provision_device_key varchar,
CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name),
CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key),
CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id),
CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id)
CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id),
CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES firmware(id)
);
-- We will use one-to-many relation in the first release and extend this feature in case of user requests
-- CREATE TABLE IF NOT EXISTS device_profile_firmware (
-- device_profile_id uuid NOT NULL,
-- firmware_id uuid NOT NULL,
-- CONSTRAINT device_profile_firmware_unq_key UNIQUE (device_profile_id, firmware_id),
-- CONSTRAINT fk_device_profile_firmware_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id) ON DELETE CASCADE,
-- CONSTRAINT fk_device_profile_firmware_firmware FOREIGN KEY (firmware_id) REFERENCES firmware(id) ON DELETE CASCADE,
-- );
CREATE TABLE IF NOT EXISTS device (
id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY,
created_time bigint NOT NULL,
@ -228,9 +242,11 @@ CREATE TABLE IF NOT EXISTS device (
search_text varchar(255),
tenant_id uuid,
firmware_id uuid,
software_id uuid,
CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name),
CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id),
CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id)
CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id),
CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES firmware(id)
);
CREATE TABLE IF NOT EXISTS device_credentials (

5
dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java

@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.DeviceProfileInfo;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.Firmware;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
@ -43,6 +44,8 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
public class BaseDeviceProfileServiceTest extends AbstractServiceTest {
private IdComparator<DeviceProfile> idComparator = new IdComparator<>();
@ -97,6 +100,8 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest {
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(savedDeviceProfile.getId());
firmware.setType(FIRMWARE);
firmware.setTitle("my firmware");
firmware.setVersion("v1.0");
firmware.setFileName("test.txt");

43
dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java

@ -20,10 +20,13 @@ import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
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;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.Firmware;
import org.thingsboard.server.common.data.Tenant;
@ -42,6 +45,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
public abstract class BaseDeviceServiceTest extends AbstractServiceTest {
@ -66,6 +70,9 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest {
tenantProfileService.deleteTenantProfiles(anotherTenantId);
}
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void testSaveDevicesWithoutMaxDeviceLimit() {
Device device = this.saveDevice(tenantId, "My device");
@ -183,6 +190,8 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest {
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(device.getDeviceProfileId());
firmware.setType(FIRMWARE);
firmware.setTitle("my firmware");
firmware.setVersion("v1.0");
firmware.setFileName("test.txt");
@ -198,6 +207,40 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest {
Device foundDevice = deviceService.findDeviceById(tenantId, savedDevice.getId());
Assert.assertEquals(foundDevice.getName(), savedDevice.getName());
}
@Test
public void testAssignFirmwareToDeviceWithDifferentDeviceProfile() {
Device device = new Device();
device.setTenantId(tenantId);
device.setName("My device");
device.setType("default");
Device savedDevice = deviceService.saveDevice(device);
Assert.assertNotNull(savedDevice);
DeviceProfile deviceProfile = createDeviceProfile(tenantId, "New device Profile");
DeviceProfile savedProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
Assert.assertNotNull(savedProfile);
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(savedProfile.getId());
firmware.setType(FIRMWARE);
firmware.setTitle("my firmware");
firmware.setVersion("v1.0");
firmware.setFileName("test.txt");
firmware.setContentType("text/plain");
firmware.setChecksumAlgorithm("sha256");
firmware.setChecksum("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a");
firmware.setData(ByteBuffer.wrap(new byte[]{1}));
Firmware savedFirmware = firmwareService.saveFirmware(firmware);
savedDevice.setFirmwareId(savedFirmware.getId());
thrown.expect(DataValidationException.class);
thrown.expectMessage("Can't assign firmware with different deviceProfile!");
deviceService.saveDevice(savedDevice);
}
@Test(expected = DataValidationException.class)
public void testSaveDeviceWithEmptyName() {

232
dao/src/test/java/org/thingsboard/server/dao/service/BaseFirmwareServiceTest.java

@ -19,13 +19,16 @@ import com.datastax.oss.driver.api.core.uuid.Uuids;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.Firmware;
import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.Tenant;
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;
@ -36,6 +39,8 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
public static final String TITLE = "My firmware";
@ -50,6 +55,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
private TenantId tenantId;
private DeviceProfileId deviceProfileId;
@Before
public void before() {
Tenant tenant = new Tenant();
@ -57,8 +64,16 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
Tenant savedTenant = tenantService.saveTenant(tenant);
Assert.assertNotNull(savedTenant);
tenantId = savedTenant.getId();
DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Device Profile");
DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
Assert.assertNotNull(savedDeviceProfile);
deviceProfileId = savedDeviceProfile.getId();
}
@Rule
public ExpectedException thrown = ExpectedException.none();
@After
public void after() {
tenantService.deleteTenant(tenantId);
@ -68,6 +83,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
public void testSaveFirmware() {
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME);
@ -99,6 +116,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
public void testSaveFirmwareInfoAndUpdateWithData() {
FirmwareInfo firmwareInfo = new FirmwareInfo();
firmwareInfo.setTenantId(tenantId);
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION);
FirmwareInfo savedFirmwareInfo = firmwareService.saveFirmwareInfo(firmwareInfo);
@ -112,6 +131,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
Firmware firmware = new Firmware(savedFirmwareInfo.getId());
firmware.setCreatedTime(firmwareInfo.getCreatedTime());
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME);
@ -135,9 +156,11 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
firmwareService.deleteFirmware(tenantId, savedFirmwareInfo.getId());
}
@Test(expected = DataValidationException.class)
@Test
public void testSaveFirmwareWithEmptyTenant() {
Firmware firmware = new Firmware();
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME);
@ -145,65 +168,108 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
firmware.setChecksum(CHECKSUM);
firmware.setData(DATA);
thrown.expect(DataValidationException.class);
thrown.expectMessage("Firmware should be assigned to tenant!");
firmwareService.saveFirmware(firmware);
}
@Test(expected = DataValidationException.class)
@Test
public void testSaveFirmwareWithEmptyType() {
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME);
firmware.setContentType(CONTENT_TYPE);
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
firmware.setChecksum(CHECKSUM);
firmware.setData(DATA);
thrown.expect(DataValidationException.class);
thrown.expectMessage("Type should be specified!");
firmwareService.saveFirmware(firmware);
}
@Test
public void testSaveFirmwareWithEmptyTitle() {
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME);
firmware.setContentType(CONTENT_TYPE);
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
firmware.setChecksum(CHECKSUM);
firmware.setData(DATA);
thrown.expect(DataValidationException.class);
thrown.expectMessage("Firmware title should be specified!");
firmwareService.saveFirmware(firmware);
}
@Test(expected = DataValidationException.class)
@Test
public void testSaveFirmwareWithEmptyFileName() {
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION);
firmware.setContentType(CONTENT_TYPE);
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
firmware.setChecksum(CHECKSUM);
firmware.setData(DATA);
thrown.expect(DataValidationException.class);
thrown.expectMessage("Firmware file name should be specified!");
firmwareService.saveFirmware(firmware);
}
@Test(expected = DataValidationException.class)
@Test
public void testSaveFirmwareWithEmptyContentType() {
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME);
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
firmware.setChecksum(CHECKSUM);
firmware.setData(DATA);
thrown.expect(DataValidationException.class);
thrown.expectMessage("Firmware content type should be specified!");
firmwareService.saveFirmware(firmware);
}
@Test(expected = DataValidationException.class)
@Test
public void testSaveFirmwareWithEmptyData() {
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME);
firmware.setContentType(CONTENT_TYPE);
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
firmware.setChecksum(CHECKSUM);
thrown.expect(DataValidationException.class);
thrown.expectMessage("Firmware data should be specified!");
firmwareService.saveFirmware(firmware);
}
@Test(expected = DataValidationException.class)
@Test
public void testSaveFirmwareWithInvalidTenant() {
Firmware firmware = new Firmware();
firmware.setTenantId(new TenantId(Uuids.timeBased()));
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME);
@ -211,41 +277,77 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
firmware.setChecksum(CHECKSUM);
firmware.setData(DATA);
thrown.expect(DataValidationException.class);
thrown.expectMessage("Firmware is referencing to non-existent tenant!");
firmwareService.saveFirmware(firmware);
}
@Test(expected = DataValidationException.class)
@Test
public void testSaveFirmwareWithInvalidDeviceProfileId() {
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(new DeviceProfileId(Uuids.timeBased()));
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME);
firmware.setContentType(CONTENT_TYPE);
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
firmware.setChecksum(CHECKSUM);
firmware.setData(DATA);
thrown.expect(DataValidationException.class);
thrown.expectMessage("Firmware is referencing to non-existent device profile!");
firmwareService.saveFirmware(firmware);
}
@Test
public void testSaveFirmwareWithEmptyChecksum() {
Firmware firmware = new Firmware();
firmware.setTenantId(new TenantId(Uuids.timeBased()));
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME);
firmware.setContentType(CONTENT_TYPE);
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
firmware.setData(DATA);
thrown.expect(DataValidationException.class);
thrown.expectMessage("Firmware checksum should be specified!");
firmwareService.saveFirmware(firmware);
}
@Test(expected = DataValidationException.class)
@Test
public void testSaveFirmwareInfoWithExistingTitleAndVersion() {
FirmwareInfo firmwareInfo = new FirmwareInfo();
firmwareInfo.setTenantId(tenantId);
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION);
firmwareService.saveFirmwareInfo(firmwareInfo);
FirmwareInfo newFirmwareInfo = new FirmwareInfo();
newFirmwareInfo.setTenantId(tenantId);
newFirmwareInfo.setDeviceProfileId(deviceProfileId);
newFirmwareInfo.setType(FIRMWARE);
newFirmwareInfo.setTitle(TITLE);
newFirmwareInfo.setVersion(VERSION);
thrown.expect(DataValidationException.class);
thrown.expectMessage("Firmware with such title and version already exists!");
firmwareService.saveFirmwareInfo(newFirmwareInfo);
}
@Test(expected = DataValidationException.class)
@Test
public void testSaveFirmwareWithExistingTitleAndVersion() {
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME);
@ -257,18 +359,27 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
Firmware newFirmware = new Firmware();
newFirmware.setTenantId(tenantId);
newFirmware.setDeviceProfileId(deviceProfileId);
newFirmware.setType(FIRMWARE);
newFirmware.setTitle(TITLE);
newFirmware.setVersion(VERSION);
newFirmware.setFileName(FILE_NAME);
newFirmware.setContentType(CONTENT_TYPE);
newFirmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
newFirmware.setChecksum(CHECKSUM);
newFirmware.setData(DATA);
thrown.expect(DataValidationException.class);
thrown.expectMessage("Firmware with such title and version already exists!");
firmwareService.saveFirmware(newFirmware);
}
@Test(expected = DataValidationException.class)
@Test
public void testDeleteFirmwareWithReferenceByDevice() {
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME);
@ -281,11 +392,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
Device device = new Device();
device.setTenantId(tenantId);
device.setName("My device");
device.setType("default");
device.setDeviceProfileId(deviceProfileId);
device.setFirmwareId(savedFirmware.getId());
Device savedDevice = deviceService.saveDevice(device);
try {
thrown.expect(DataValidationException.class);
thrown.expectMessage("The firmware referenced by the devices cannot be deleted!");
firmwareService.deleteFirmware(tenantId, savedFirmware.getId());
} finally {
deviceService.deleteDevice(tenantId, savedDevice.getId());
@ -293,10 +406,12 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
}
}
@Test(expected = DataValidationException.class)
public void testDeleteFirmwareWithReferenceByDeviceProfile() {
@Test
public void testUpdateDeviceProfileIdWithReferenceByDevice() {
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME);
@ -306,12 +421,81 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
firmware.setData(DATA);
Firmware savedFirmware = firmwareService.saveFirmware(firmware);
DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Device Profile");
deviceProfile.setFirmwareId(savedFirmware.getId());
Device device = new Device();
device.setTenantId(tenantId);
device.setName("My device");
device.setDeviceProfileId(deviceProfileId);
device.setFirmwareId(savedFirmware.getId());
Device savedDevice = deviceService.saveDevice(device);
try {
thrown.expect(DataValidationException.class);
thrown.expectMessage("Can`t update deviceProfileId because firmware is already in use!");
savedFirmware.setDeviceProfileId(null);
firmwareService.saveFirmware(savedFirmware);
} finally {
deviceService.deleteDevice(tenantId, savedDevice.getId());
firmwareService.deleteFirmware(tenantId, savedFirmware.getId());
}
}
@Test
public void testDeleteFirmwareWithReferenceByDeviceProfile() {
DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Test Device Profile");
DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(savedDeviceProfile.getId());
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME);
firmware.setContentType(CONTENT_TYPE);
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
firmware.setChecksum(CHECKSUM);
firmware.setData(DATA);
Firmware savedFirmware = firmwareService.saveFirmware(firmware);
savedDeviceProfile.setFirmwareId(savedFirmware.getId());
deviceProfileService.saveDeviceProfile(savedDeviceProfile);
try {
thrown.expect(DataValidationException.class);
thrown.expectMessage("The firmware referenced by the device profile cannot be deleted!");
firmwareService.deleteFirmware(tenantId, savedFirmware.getId());
} finally {
deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId());
firmwareService.deleteFirmware(tenantId, savedFirmware.getId());
}
}
@Test
public void testUpdateDeviceProfileIdWithReferenceByDeviceProfile() {
DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Test Device Profile");
DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(savedDeviceProfile.getId());
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME);
firmware.setContentType(CONTENT_TYPE);
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
firmware.setChecksum(CHECKSUM);
firmware.setData(DATA);
Firmware savedFirmware = firmwareService.saveFirmware(firmware);
savedDeviceProfile.setFirmwareId(savedFirmware.getId());
deviceProfileService.saveDeviceProfile(savedDeviceProfile);
try {
thrown.expect(DataValidationException.class);
thrown.expectMessage("Can`t update deviceProfileId because firmware is already in use!");
savedFirmware.setDeviceProfileId(null);
firmwareService.saveFirmware(savedFirmware);
} finally {
deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId());
firmwareService.deleteFirmware(tenantId, savedFirmware.getId());
@ -322,6 +506,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
public void testFindFirmwareById() {
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME);
@ -341,6 +527,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
public void testFindFirmwareInfoById() {
FirmwareInfo firmware = new FirmwareInfo();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION);
FirmwareInfo savedFirmware = firmwareService.saveFirmwareInfo(firmware);
@ -355,6 +543,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
public void testDeleteFirmware() {
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME);
@ -377,6 +567,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
for (int i = 0; i < 165; i++) {
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION + i);
firmware.setFileName(FILE_NAME);
@ -420,6 +612,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
for (int i = 0; i < 165; i++) {
FirmwareInfo firmwareInfo = new FirmwareInfo();
firmwareInfo.setTenantId(tenantId);
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION + i);
firmwareInfo.setFileName(FILE_NAME);
@ -434,7 +628,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
PageLink pageLink = new PageLink(16);
PageData<FirmwareInfo> pageData;
do {
pageData = firmwareService.findTenantFirmwaresByTenantIdAndHasData(tenantId, false, pageLink);
pageData = firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, false, pageLink);
loadedFirmwares.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
@ -450,6 +644,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
Firmware firmware = new Firmware(f.getId());
firmware.setCreatedTime(f.getCreatedTime());
firmware.setTenantId(f.getTenantId());
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(f.getTitle());
firmware.setVersion(f.getVersion());
firmware.setFileName(FILE_NAME);
@ -465,7 +661,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
loadedFirmwares = new ArrayList<>();
pageLink = new PageLink(16);
do {
pageData = firmwareService.findTenantFirmwaresByTenantIdAndHasData(tenantId, true, pageLink);
pageData = firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, true, pageLink);
loadedFirmwares.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();

0
transport/lwm2m/src/main/data/credentials/serverKeyStore.jks → transport/lwm2m/src/main/data/lwm2mserver.jks

2
ui-ngx/src/app/core/http/entity.service.ts

@ -361,7 +361,7 @@ export class EntityService {
break;
case EntityType.FIRMWARE:
pageLink.sortOrder.property = 'title';
entitiesObservable = this.firmwareService.getFirmwares(pageLink, true, config);
entitiesObservable = this.firmwareService.getFirmwares(pageLink, config);
break;
}
return entitiesObservable;

15
ui-ngx/src/app/core/http/firmware.service.ts

@ -20,7 +20,7 @@ import { PageLink } from '@shared/models/page/page-link';
import { defaultHttpOptionsFromConfig, defaultHttpUploadOptions, RequestConfig } from '@core/http/http-utils';
import { Observable } from 'rxjs';
import { PageData } from '@shared/models/page/page-data';
import { Firmware, FirmwareInfo } from '@shared/models/firmware.models';
import { Firmware, FirmwareInfo, FirmwareType } from '@shared/models/firmware.models';
import { catchError, map, mergeMap } from 'rxjs/operators';
import { deepClone, isDefinedAndNotNull } from '@core/utils';
@ -34,12 +34,13 @@ export class FirmwareService {
}
public getFirmwares(pageLink: PageLink, hasData?: boolean, config?: RequestConfig): Observable<PageData<FirmwareInfo>> {
let url = `/api/firmwares`;
if (isDefinedAndNotNull(hasData)) {
url += `/${hasData}`;
}
url += `${pageLink.toQuery()}`;
public getFirmwares(pageLink: PageLink, config?: RequestConfig): Observable<PageData<FirmwareInfo>> {
return this.http.get<PageData<FirmwareInfo>>(`/api/firmwares${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config));
}
public getFirmwaresInfoByDeviceProfileId(pageLink: PageLink, deviceProfileId: string, type: FirmwareType,
hasData = true, config?: RequestConfig): Observable<PageData<FirmwareInfo>> {
const url = `/api/firmwares/${deviceProfileId}/${type}/${hasData}${pageLink.toQuery()}`;
return this.http.get<PageData<FirmwareInfo>>(url, defaultHttpOptionsFromConfig(config));
}

94
ui-ngx/src/app/core/services/menu.service.ts

@ -103,19 +103,12 @@ export class MenuService {
path: '/widgets-bundles',
icon: 'now_widgets'
},
{
id: guid(),
name: 'resource.resources-library',
type: 'link',
path: '/resources-library',
icon: 'folder'
},
{
id: guid(),
name: 'admin.system-settings',
type: 'toggle',
path: '/settings',
height: '200px',
height: '240px',
icon: 'settings',
pages: [
{
@ -152,6 +145,13 @@ export class MenuService {
type: 'link',
path: '/settings/oauth2',
icon: 'security'
},
{
id: guid(),
name: 'resource.resources-library',
type: 'link',
path: '/settings/resources-library',
icon: 'folder'
}
]
}
@ -188,16 +188,6 @@ export class MenuService {
}
]
},
{
name: 'resource.management',
places: [
{
name: 'resource.resources-library',
icon: 'folder',
path: '/resources-library'
}
]
},
{
name: 'admin.system-settings',
places: [
@ -225,6 +215,11 @@ export class MenuService {
name: 'admin.oauth2.oauth2',
icon: 'security',
path: '/settings/oauth2'
},
{
name: 'resource.resources-library',
icon: 'folder',
path: '/resources-library'
}
]
}
@ -337,20 +332,6 @@ export class MenuService {
path: '/dashboards',
icon: 'dashboards'
},
{
id: guid(),
name: 'resource.resources-library',
type: 'link',
path: '/resources-library',
icon: 'folder'
},
{
id: guid(),
name: 'admin.home-settings',
type: 'link',
path: '/settings/home',
icon: 'settings_applications'
},
{
id: guid(),
name: 'audit-log.audit-logs',
@ -365,6 +346,30 @@ export class MenuService {
path: '/usage',
icon: 'insert_chart',
notExact: true
},
{
id: guid(),
name: 'admin.system-settings',
type: 'toggle',
path: '/settings',
height: '80px',
icon: 'settings',
pages: [
{
id: guid(),
name: 'admin.home-settings',
type: 'link',
path: '/settings/home',
icon: 'settings_applications'
},
{
id: guid(),
name: 'resource.resources-library',
type: 'link',
path: '/settings/resources-library',
icon: 'folder'
}
]
}
);
return sections;
@ -455,16 +460,6 @@ export class MenuService {
);
}
homeSections.push(
{
name: 'resource.management',
places: [
{
name: 'resource.resources-library',
icon: 'folder',
path: '/resources-library'
}
]
},
{
name: 'dashboard.management',
places: [
@ -494,6 +489,21 @@ export class MenuService {
path: '/usage'
}
]
},
{
name: 'admin.system-settings',
places: [
{
name: 'admin.home-settings',
icon: 'settings_applications',
path: '/settings/home'
},
{
name: 'resource.resources-library',
icon: 'folder',
path: '/settings/resources-library'
}
]
}
);
return homeSections;

4
ui-ngx/src/app/modules/home/components/device/device-credentials.component.html

@ -20,7 +20,7 @@
<mat-label translate>device.credentials-type</mat-label>
<mat-select formControlName="credentialsType">
<mat-option *ngFor="let credentialsType of credentialsTypes" [value]="credentialsType">
{{ credentialTypeNamesMap.get(deviceCredentialsType[credentialsType]) }}
{{ credentialTypeNamesMap.get(credentialsType) }}
</mat-option>
</mat-select>
</mat-form-field>
@ -85,7 +85,7 @@
<mat-form-field class="mat-block">
<mat-label translate>device.lwm2m-value</mat-label>
<textarea matInput formControlName="credentialsValue" rows="10" required
[matTooltip]="lwm2mCredentialsValueTip(deviceCredentialsFormGroup.get('credentialsValue').hasError('jsonError'))"
[matTooltip]="lwm2mCredentialsValueTooltip(deviceCredentialsFormGroup.get('credentialsValue').hasError('jsonError'))"
matTooltipPosition="above"
></textarea>
<mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsValue').hasError('required')">

75
ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts

@ -14,7 +14,7 @@
/// limitations under the License.
///
import {Component, forwardRef, Input, OnDestroy, OnInit} from '@angular/core';
import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
import {
ControlValueAccessor,
FormBuilder,
@ -33,9 +33,9 @@ import {
DeviceCredentials,
DeviceCredentialsType
} from '@shared/models/device.models';
import {Subscription} from 'rxjs';
import {distinctUntilChanged} from 'rxjs/operators';
import {SecurityConfigComponent} from '@home/pages/device/lwm2m/security-config.component';
import { Subject } from 'rxjs';
import { distinctUntilChanged, takeUntil } from 'rxjs/operators';
import { SecurityConfigLwm2mComponent } from '@home/components/device/security-config-lwm2m.component';
import {
ClientSecurityConfig,
DEFAULT_END_POINT,
@ -44,10 +44,10 @@ import {
getDefaultSecurityConfig,
JSON_ALL_CONFIG,
validateSecurityConfig
} from '@home/pages/device/lwm2m/security-config.models';
import {TranslateService} from '@ngx-translate/core';
import {MatDialog} from '@angular/material/dialog';
import {isDefinedAndNotNull} from '@core/utils';
} from '@shared/models/lwm2m-security-config.models';
import { TranslateService } from '@ngx-translate/core';
import { MatDialog } from '@angular/material/dialog';
import { isDefinedAndNotNull } from '@core/utils';
@Component({
selector: 'tb-device-credentials',
@ -67,20 +67,16 @@ import {isDefinedAndNotNull} from '@core/utils';
})
export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, Validator, OnDestroy {
deviceCredentialsFormGroup: FormGroup;
subscriptions: Subscription[] = [];
@Input()
disabled: boolean;
deviceCredentials: DeviceCredentials = null;
private destroy$ = new Subject();
submitted = false;
deviceCredentialsFormGroup: FormGroup;
deviceCredentialsType = DeviceCredentialsType;
credentialsTypes = Object.keys(DeviceCredentialsType);
credentialsTypes = Object.values(DeviceCredentialsType);
credentialTypeNamesMap = credentialTypeNames;
@ -102,16 +98,17 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
}, {validators: this.atLeastOne(Validators.required, ['clientId', 'userName'])})
});
this.deviceCredentialsFormGroup.get('credentialsBasic').disable();
this.subscriptions.push(
this.deviceCredentialsFormGroup.valueChanges.pipe(distinctUntilChanged()).subscribe(() => {
this.updateView();
})
);
this.subscriptions.push(
this.deviceCredentialsFormGroup.get('credentialsType').valueChanges.subscribe(() => {
this.credentialsTypeChanged();
})
);
this.deviceCredentialsFormGroup.valueChanges.pipe(
distinctUntilChanged(),
takeUntil(this.destroy$)
).subscribe(() => {
this.updateView();
});
this.deviceCredentialsFormGroup.get('credentialsType').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((type) => {
this.credentialsTypeChanged(type);
});
}
ngOnInit(): void {
@ -121,12 +118,12 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
}
ngOnDestroy() {
this.subscriptions.forEach(s => s.unsubscribe());
this.destroy$.next();
this.destroy$.complete();
}
writeValue(value: DeviceCredentials | null): void {
if (isDefinedAndNotNull(value)) {
this.deviceCredentials = value;
let credentialsBasic = {clientId: null, userName: null, password: null};
let credentialsValue = null;
if (value.credentialsType === DeviceCredentialsType.MQTT_BASIC) {
@ -179,10 +176,11 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
};
}
credentialsTypeChanged(): void {
credentialsTypeChanged(credentialsType: DeviceCredentialsType): void {
const credentialsValue = credentialsType === DeviceCredentialsType.LWM2M_CREDENTIALS ? this.lwm2mDefaultConfig : null;
this.deviceCredentialsFormGroup.patchValue({
credentialsId: null,
credentialsValue: JSON.stringify(getDefaultSecurityConfig(), null, 2),
credentialsValue,
credentialsBasic: {clientId: '', userName: '', password: ''}
});
this.updateValidators();
@ -264,7 +262,7 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
}
}
const credentialsId = this.deviceCredentialsFormGroup.get('credentialsId').value || DEFAULT_END_POINT;
this.dialog.open<SecurityConfigComponent, DeviceCredentialsDialogLwm2mData, object>(SecurityConfigComponent, {
this.dialog.open<SecurityConfigLwm2mComponent, DeviceCredentialsDialogLwm2mData, object>(SecurityConfigLwm2mComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
@ -275,8 +273,8 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
(res) => {
if (res) {
this.deviceCredentialsFormGroup.patchValue({
credentialsValue: this.isDefautLw2mResponse(res[JSON_ALL_CONFIG]) ? null : JSON.stringify(res[JSON_ALL_CONFIG]),
credentialsId: this.isDefautLw2mResponse(res[END_POINT]) ? null : JSON.stringify(res[END_POINT]).split('\"').join('')
credentialsValue: this.isDefaultLw2mResponse(res[JSON_ALL_CONFIG]) ? null : JSON.stringify(res[JSON_ALL_CONFIG]),
credentialsId: this.isDefaultLw2mResponse(res[END_POINT]) ? null : JSON.stringify(res[END_POINT]).split('\"').join('')
});
this.deviceCredentialsFormGroup.get('credentialsValue').markAsDirty();
}
@ -284,16 +282,19 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
);
}
private isDefautLw2mResponse(response: object): boolean {
private isDefaultLw2mResponse(response: object): boolean {
return Object.keys(response).length === 0 || JSON.stringify(response) === '[{}]';
}
private lwm2mConfigJsonValidator(control: FormControl) {
return validateSecurityConfig(control.value) ? null: {jsonError: {parsedJson: "error"}};
return validateSecurityConfig(control.value) ? null : {jsonError: {parsedJson: 'error'}};
}
private get lwm2mDefaultConfig(): string {
return JSON.stringify(getDefaultSecurityConfig(), null, 2);
}
lwm2mCredentialsValueTip (flag: boolean): string {
let jsonConfigDef = JSON.stringify(getDefaultSecurityConfig(), null, 2);
return !flag ? "" : 'Example (mode=\"NoSec\"):\n\r ' + jsonConfigDef;
lwm2mCredentialsValueTooltip(flag: boolean): string {
return !flag ? '' : 'Example (mode=\"NoSec\"):\n\r ' + this.lwm2mDefaultConfig;
}
}

79
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.html

@ -0,0 +1,79 @@
<!--
Copyright © 2016-2021 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.
-->
<section [formGroup]="serverFormGroup" fxLayout="column">
<mat-form-field class="mat-block">
<mat-label translate>device.lwm2m-security-config.mode</mat-label>
<mat-select formControlName="securityMode">
<mat-option *ngFor="let securityType of securityConfigLwM2MTypes" [value]="securityType">
{{ lwm2mSecurityTypeTranslationMap.get(securityType) }}
</mat-option>
</mat-select>
</mat-form-field>
<div [fxShow]="serverFormGroup.get('securityMode').value !== securityConfigLwM2MType.NO_SEC">
<mat-form-field class="mat-block">
<mat-label>{{ 'device.lwm2m-security-config.client-publicKey-or-id' | translate }}</mat-label>
<textarea matInput
#clientPublicKeyOrId
[maxlength]="lenMaxClientPublicKeyOrId"
cdkTextareaAutosize
cdkAutosizeMinRows="1"
cols="1"
formControlName="clientPublicKeyOrId"
required>
</textarea>
<mat-hint align="end">{{clientPublicKeyOrId.value?.length || 0}}/{{lenMaxClientPublicKeyOrId}}</mat-hint>
<mat-error *ngIf="serverFormGroup.get('clientPublicKeyOrId').hasError('required')">
{{ 'device.lwm2m-security-config.client-publicKey-or-id-required' | translate }}
</mat-error>
<mat-error *ngIf="serverFormGroup.get('clientPublicKeyOrId').hasError('pattern')">
{{ 'device.lwm2m-security-config.client-publicKey-or-id-pattern' | translate }}
</mat-error>
<mat-error *ngIf="(serverFormGroup.get('clientPublicKeyOrId').hasError('maxlength') ||
serverFormGroup.get('clientPublicKeyOrId').hasError('minlength'))">
{{ 'device.lwm2m-security-config.client-publicKey-or-id-length' | translate: {
count: lenMaxClientPublicKeyOrId
} }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label>{{ 'device.lwm2m-security-config.client-secret-key' | translate }}</mat-label>
<textarea matInput
#clientSecretKey
[maxlength]="lengthClientSecretKey"
cdkTextareaAutosize
cdkAutosizeMinRows="1"
cols="1"
formControlName="clientSecretKey"
required>
</textarea>
<mat-hint align="end">{{clientSecretKey.value?.length || 0}}/{{lengthClientSecretKey}}</mat-hint>
<mat-error *ngIf="serverFormGroup.get('clientSecretKey').hasError('required')">
{{ 'device.lwm2m-security-config.client-secret-key-required' | translate }}
</mat-error>
<mat-error *ngIf="serverFormGroup.get('clientSecretKey').hasError('pattern')">
{{ 'device.lwm2m-security-config.client-secret-key-pattern' | translate }}
</mat-error>
<mat-error *ngIf="(serverFormGroup.get('clientSecretKey').hasError('maxlength') ||
serverFormGroup.get('clientSecretKey').hasError('minlength'))">
{{ 'device.lwm2m-security-config.client-secret-key-length' | translate: {
count: lengthClientSecretKey
} }}
</mat-error>
</mat-form-field>
</div>
</section>

20
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.scss

@ -0,0 +1,20 @@
/**
* Copyright © 2016-2021 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.
*/
:host ::ng-deep {
textarea.mat-input-element.cdk-textarea-autosize {
box-sizing: content-box;
}
}

176
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.ts

@ -0,0 +1,176 @@
///
/// Copyright © 2016-2021 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.
///
import { Component, forwardRef, OnDestroy } from '@angular/core';
import {
ControlValueAccessor,
FormBuilder,
FormGroup,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
ValidationErrors,
Validator,
Validators
} from '@angular/forms';
import {
KEY_REGEXP_HEX_DEC,
LEN_MAX_PRIVATE_KEY,
LEN_MAX_PSK,
LEN_MAX_PUBLIC_KEY_RPK,
LEN_MAX_PUBLIC_KEY_X509,
Lwm2mSecurityType,
Lwm2mSecurityTypeTranslationMap,
ServerSecurityConfig
} from '@shared/models/lwm2m-security-config.models';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
@Component({
selector: 'tb-security-config-lwm2m-server',
templateUrl: './security-config-lwm2m-server.component.html',
styleUrls: ['./security-config-lwm2m-server.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SecurityConfigLwm2mServerComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => SecurityConfigLwm2mServerComponent),
multi: true
}
]
})
export class SecurityConfigLwm2mServerComponent implements OnDestroy, ControlValueAccessor, Validator {
serverFormGroup: FormGroup;
securityConfigLwM2MType = Lwm2mSecurityType;
securityConfigLwM2MTypes = Object.values(Lwm2mSecurityType);
lwm2mSecurityTypeTranslationMap = Lwm2mSecurityTypeTranslationMap;
lenMinClientPublicKeyOrId = 0;
lenMaxClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_RPK;
lengthClientSecretKey = LEN_MAX_PRIVATE_KEY;
private destroy$ = new Subject();
private propagateChange = (v: any) => {};
constructor(private fb: FormBuilder) {
this.serverFormGroup = this.fb.group({
securityMode: [Lwm2mSecurityType.NO_SEC],
clientPublicKeyOrId: [''],
clientSecretKey: ['']
});
this.serverFormGroup.get('securityMode').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((securityMode) => {
this.updateValidate(securityMode);
});
this.serverFormGroup.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((value) => {
this.propagateChange(value);
});
}
writeValue(value: any): void {
if (value) {
this.updateValueFields(value);
}
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
setDisabledState(isDisabled: boolean): void {
if (isDisabled) {
this.serverFormGroup.disable({emitEvent: false});
} else {
this.serverFormGroup.enable({emitEvent: false});
}
}
validate(control): ValidationErrors | null {
return this.serverFormGroup.valid ? null : {
securityConfig: {valid: false}
};
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
private updateValueFields(serverData: ServerSecurityConfig): void {
this.serverFormGroup.patchValue(serverData, {emitEvent: false});
this.updateValidate(serverData.securityMode, true);
}
private updateValidate(securityMode: Lwm2mSecurityType, initValue = false): void {
switch (securityMode) {
case Lwm2mSecurityType.NO_SEC:
this.serverFormGroup.get('clientPublicKeyOrId').clearValidators();
this.serverFormGroup.get('clientSecretKey').clearValidators();
break;
case Lwm2mSecurityType.PSK:
this.lenMinClientPublicKeyOrId = 0;
this.lenMaxClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_RPK;
this.lengthClientSecretKey = LEN_MAX_PSK;
this.setValidatorsSecurity(securityMode);
break;
case Lwm2mSecurityType.RPK:
this.lenMinClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_RPK;
this.lenMaxClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_RPK;
this.lengthClientSecretKey = LEN_MAX_PRIVATE_KEY;
this.setValidatorsSecurity(securityMode);
break;
case Lwm2mSecurityType.X509:
this.lenMinClientPublicKeyOrId = 0;
this.lenMaxClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_X509;
this.lengthClientSecretKey = LEN_MAX_PRIVATE_KEY;
this.setValidatorsSecurity(securityMode);
break;
}
this.serverFormGroup.get('clientPublicKeyOrId').updateValueAndValidity({emitEvent: false});
this.serverFormGroup.get('clientSecretKey').updateValueAndValidity({emitEvent: !initValue});
}
private setValidatorsSecurity = (securityMode: Lwm2mSecurityType): void => {
if (securityMode === Lwm2mSecurityType.PSK) {
this.serverFormGroup.get('clientPublicKeyOrId').setValidators([Validators.required]);
} else {
this.serverFormGroup.get('clientPublicKeyOrId').setValidators([
Validators.required,
Validators.pattern(KEY_REGEXP_HEX_DEC),
Validators.minLength(this.lenMinClientPublicKeyOrId),
Validators.maxLength(this.lenMaxClientPublicKeyOrId)
]);
}
this.serverFormGroup.get('clientSecretKey').setValidators([
Validators.required,
Validators.pattern(KEY_REGEXP_HEX_DEC),
Validators.minLength(this.lengthClientSecretKey),
Validators.maxLength(this.lengthClientSecretKey)
]);
}
}

143
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.html

@ -0,0 +1,143 @@
<!--
Copyright © 2016-2021 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.
-->
<form [formGroup]="lwm2mConfigFormGroup" (ngSubmit)="save()" style="min-width: 400px;">
<mat-toolbar fxLayout="row" color="primary">
<h2>{{ title }}</h2>
<span fxFlex></span>
<button mat-button mat-icon-button
(click)="cancel()"
type="button">
<mat-icon class="material-icons">close</mat-icon>
</button>
</mat-toolbar>
<div mat-dialog-content>
<mat-form-field class="mat-block">
<mat-label translate>device.lwm2m-security-config.endpoint</mat-label>
<input matInput type="text" formControlName="endPoint" required>
<mat-error *ngIf="lwm2mConfigFormGroup.get('endPoint').hasError('required')">
{{ 'device.lwm2m-security-config.endpoint-required' | translate }}
</mat-error>
</mat-form-field>
<mat-tab-group (selectedTabChange)="tabChanged($event)">
<mat-tab label="{{ 'device.lwm2m-security-config.client-tab' | translate }}">
<mat-form-field class="mat-block">
<mat-label translate>device.lwm2m-security-config.mode</mat-label>
<mat-select formControlName="securityConfigClientMode">
<mat-option *ngFor="let securityConfigClientMode of securityConfigLwM2MTypes"
[value]="securityConfigClientMode">
{{ credentialTypeLwM2MNamesMap.get(securityConfigLwM2MType[securityConfigClientMode]) }}
</mat-option>
</mat-select>
</mat-form-field>
<div *ngIf="lwm2mConfigFormGroup.get('securityConfigClientMode').value === securityConfigLwM2MType.PSK">
<mat-form-field class="mat-block">
<mat-label>{{ 'device.lwm2m-security-config.identity' | translate }}</mat-label>
<input matInput type="text" formControlName="identity" required>
<mat-error *ngIf="lwm2mConfigFormGroup.get('identity').hasError('required')">
{{ 'device.lwm2m-security-config.identity-required' | translate }}
</mat-error>
</mat-form-field>
</div>
<mat-form-field class="mat-block" *ngIf="lwm2mConfigFormGroup.get('securityConfigClientMode').value === securityConfigLwM2MType.RPK ||
lwm2mConfigFormGroup.get('securityConfigClientMode').value === securityConfigLwM2MType.PSK">
<mat-label>{{ 'device.lwm2m-security-config.client-key' | translate }}</mat-label>
<textarea matInput
#key
maxlength="{{lenMaxKeyClient}}"
cdkTextareaAutosize
cdkAutosizeMinRows="1"
cols="1"
formControlName="key"
required>
</textarea>
<mat-hint align="end">{{key.value?.length || 0}}/{{lenMaxKeyClient}}</mat-hint>
<mat-error *ngIf="lwm2mConfigFormGroup.get('key').hasError('required')">
{{ 'device.lwm2m-security-config.client-key-required' | translate }}
</mat-error>
<mat-error *ngIf="lwm2mConfigFormGroup.get('key').hasError('pattern')">
{{ 'device.lwm2m-security-config.client-key-pattern' | translate }}
</mat-error>
<mat-error *ngIf="(lwm2mConfigFormGroup.get('key').hasError('maxlength') ||
lwm2mConfigFormGroup.get('key').hasError('minlength'))">
{{ 'device.lwm2m-security-config.client-key-length' | translate: {
count: lenMaxKeyClient
} }}
</mat-error>
</mat-form-field>
<mat-checkbox formControlName="x509" color="primary"
*ngIf="lwm2mConfigFormGroup.get('securityConfigClientMode').value === securityConfigLwM2MType.X509">
{{ 'device.lwm2m-security-config.client-certificate' | translate }}
</mat-checkbox>
</mat-tab>
<mat-tab label="{{ 'device.lwm2m-security-config.bootstrap-tab' | translate }}">
<div style="padding: 2px;">
<mat-accordion multi="true" class="mat-body-1">
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
{{ 'device.lwm2m-security-config.bootstrap-server' | translate }}
</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-security-config-lwm2m-server
formControlName="bootstrapServer">
</tb-security-config-lwm2m-server>
</ng-template>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
{{ 'device.lwm2m-security-config.lwm2m-server' | translate }}
</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-security-config-lwm2m-server
formControlName="lwm2mServer">
</tb-security-config-lwm2m-server>
</ng-template>
</mat-expansion-panel>
</mat-accordion>
</div>
</mat-tab>
<mat-tab label="{{ 'device.lwm2m-security-config.config-json-tab' | translate }}">
<ng-template matTabContent>
<tb-json-object-edit
[formControlName]="formControlNameJsonAllConfig"
label="{{ 'device.lwm2m-value' | translate }}"
readonly
[required]="true"
[fillHeight]="false">
</tb-json-object-edit>
</ng-template>
</mat-tab>
</mat-tab-group>
</div>
<div mat-dialog-actions fxLayout="row" fxLayoutAlign="end center">
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"
(click)="cancel()">
{{ 'action.cancel' | translate }}
</button>
<button mat-button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || lwm2mConfigFormGroup.invalid || !lwm2mConfigFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
</div>
</form>

34
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.scss

@ -0,0 +1,34 @@
/**
* Copyright © 2016-2021 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.
*/
:host {
mat-tab-group {
min-height: 330px;
}
}
:host ::ng-deep {
.mat-tab-body-wrapper {
min-height: 200px;
}
.mat-tab-body {
padding: 16px 0;
}
textarea.mat-input-element.cdk-textarea-autosize {
box-sizing: content-box;
}
}

257
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.ts

@ -0,0 +1,257 @@
///
/// Copyright © 2016-2021 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.
///
import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
import { DialogComponent } from '@shared/components/dialog.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { Router } from '@angular/router';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core';
import {
DeviceCredentialsDialogLwm2mData,
getClientSecurityConfig,
JSON_ALL_CONFIG,
KEY_REGEXP_HEX_DEC,
LEN_MAX_PSK,
LEN_MAX_PUBLIC_KEY_RPK,
Lwm2mSecurityConfigModels,
Lwm2mSecurityType,
Lwm2mSecurityTypeTranslationMap
} from '@shared/models/lwm2m-security-config.models';
import { MatTabChangeEvent } from '@angular/material/tabs';
import { MatTab } from '@angular/material/tabs/tab';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'tb-security-config-lwm2m',
templateUrl: './security-config-lwm2m.component.html',
styleUrls: ['./security-config-lwm2m.component.scss']
})
export class SecurityConfigLwm2mComponent extends DialogComponent<SecurityConfigLwm2mComponent, object> implements OnInit, OnDestroy {
private destroy$ = new Subject();
lwm2mConfigFormGroup: FormGroup;
title: string;
securityConfigLwM2MType = Lwm2mSecurityType;
securityConfigLwM2MTypes = Object.keys(Lwm2mSecurityType);
credentialTypeLwM2MNamesMap = Lwm2mSecurityTypeTranslationMap;
formControlNameJsonAllConfig = JSON_ALL_CONFIG;
jsonAllConfig: Lwm2mSecurityConfigModels;
lenMaxKeyClient = LEN_MAX_PSK;
tabPrevious: MatTab;
tabIndexPrevious = 0;
constructor(protected store: Store<AppState>,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: DeviceCredentialsDialogLwm2mData,
public dialogRef: MatDialogRef<SecurityConfigLwm2mComponent, object>,
public fb: FormBuilder,
public translate: TranslateService) {
super(store, router, dialogRef);
}
ngOnInit() {
this.jsonAllConfig = JSON.parse(JSON.stringify(this.data.jsonAllConfig));
this.lwm2mConfigFormGroup = this.initLwm2mConfigFormGroup();
this.title = this.translate.instant('device.lwm2m-security-info') + ': ' + this.data.endPoint;
this.lwm2mConfigFormGroup.get('x509').disable();
this.initClientSecurityConfig(this.lwm2mConfigFormGroup.get('jsonAllConfig').value);
this.registerDisableOnLoadFormControl(this.lwm2mConfigFormGroup.get('securityConfigClientMode'));
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
private initClientSecurityConfig = (jsonAllConfig: Lwm2mSecurityConfigModels): void => {
if (jsonAllConfig.client.securityConfigClientMode !== Lwm2mSecurityType.NO_SEC) {
this.lwm2mConfigFormGroup.patchValue(jsonAllConfig.client, {emitEvent: false});
}
this.securityConfigClientUpdateValidators(jsonAllConfig.client.securityConfigClientMode);
}
private securityConfigClientModeChanged(type: Lwm2mSecurityType): void {
const config = getClientSecurityConfig(type, this.lwm2mConfigFormGroup.get('endPoint').value);
switch (type) {
case Lwm2mSecurityType.PSK:
config.identity = this.data.endPoint;
config.key = this.lwm2mConfigFormGroup.get('key').value;
break;
case Lwm2mSecurityType.RPK:
config.key = this.lwm2mConfigFormGroup.get('key').value;
break;
}
this.jsonAllConfig.client = config;
this.lwm2mConfigFormGroup.patchValue({
...config,
jsonAllConfig: this.jsonAllConfig
}, {emitEvent: false});
this.securityConfigClientUpdateValidators(type);
}
private securityConfigClientUpdateValidators = (mode: Lwm2mSecurityType): void => {
switch (mode) {
case Lwm2mSecurityType.NO_SEC:
case Lwm2mSecurityType.X509:
this.setValidatorsNoSecX509();
break;
case Lwm2mSecurityType.PSK:
this.lenMaxKeyClient = LEN_MAX_PSK;
this.setValidatorsPskRpk(mode);
break;
case Lwm2mSecurityType.RPK:
this.lenMaxKeyClient = LEN_MAX_PUBLIC_KEY_RPK;
this.setValidatorsPskRpk(mode);
break;
}
this.lwm2mConfigFormGroup.get('identity').updateValueAndValidity({emitEvent: false});
this.lwm2mConfigFormGroup.get('key').updateValueAndValidity({emitEvent: false});
}
private setValidatorsNoSecX509 = (): void => {
this.lwm2mConfigFormGroup.get('identity').setValidators([]);
this.lwm2mConfigFormGroup.get('key').setValidators([]);
}
private setValidatorsPskRpk = (mode: Lwm2mSecurityType): void => {
if (mode === Lwm2mSecurityType.PSK) {
this.lwm2mConfigFormGroup.get('identity').setValidators([Validators.required]);
} else {
this.lwm2mConfigFormGroup.get('identity').setValidators([]);
}
this.lwm2mConfigFormGroup.get('key').setValidators([Validators.required,
Validators.pattern(KEY_REGEXP_HEX_DEC),
Validators.maxLength(this.lenMaxKeyClient), Validators.minLength(this.lenMaxKeyClient)]);
}
tabChanged = (tabChangeEvent: MatTabChangeEvent): void => {
if (this.tabIndexPrevious !== tabChangeEvent.index) {
this.upDateValueToJson();
}
this.tabIndexPrevious = tabChangeEvent.index;
}
private upDateValueToJson(): void {
switch (this.tabIndexPrevious) {
case 0:
this.upDateValueToJsonTab0();
break;
case 1:
this.upDateValueToJsonTab1();
break;
}
}
private upDateValueToJsonTab0 = (): void => {
if (this.lwm2mConfigFormGroup.get('identity').dirty && this.lwm2mConfigFormGroup.get('identity').valid ||
this.lwm2mConfigFormGroup.get('key').dirty && this.lwm2mConfigFormGroup.get('key').valid) {
this.updateBootstrapSettings();
this.upDateJsonAllConfig();
}
}
private upDateValueToJsonTab1 = (): void => {
const bootstrap = this.lwm2mConfigFormGroup.get('bootstrapServer').value;
if (bootstrap !== null
&& this.lwm2mConfigFormGroup.get('bootstrapServer').dirty
&& this.lwm2mConfigFormGroup.get('bootstrapServer').valid) {
this.jsonAllConfig.bootstrap.bootstrapServer = bootstrap;
this.upDateJsonAllConfig();
}
const serverConfig = this.lwm2mConfigFormGroup.get('lwm2mServer').value;
if (serverConfig !== null
&& this.lwm2mConfigFormGroup.get('lwm2mServer').dirty
&& this.lwm2mConfigFormGroup.get('lwm2mServer').valid) {
this.jsonAllConfig.bootstrap.lwm2mServer = serverConfig;
this.upDateJsonAllConfig();
}
}
private updateBootstrapSettings() {
const securityMode = 'securityMode';
this.jsonAllConfig.client.identity = this.lwm2mConfigFormGroup.get('identity').value;
this.jsonAllConfig.client.key = this.lwm2mConfigFormGroup.get('key').value;
if (this.lwm2mConfigFormGroup.get('bootstrapServer').value[securityMode] === Lwm2mSecurityType.PSK) {
this.jsonAllConfig.bootstrap.bootstrapServer.clientPublicKeyOrId = this.jsonAllConfig.client.identity;
this.jsonAllConfig.bootstrap.bootstrapServer.clientSecretKey = this.jsonAllConfig.client.key;
this.lwm2mConfigFormGroup.get('bootstrapServer').patchValue(this.jsonAllConfig.bootstrap.bootstrapServer, {emitEvent: false});
}
if (this.lwm2mConfigFormGroup.get('lwm2mServer').value[securityMode] === Lwm2mSecurityType.PSK) {
this.jsonAllConfig.bootstrap.lwm2mServer.clientPublicKeyOrId = this.jsonAllConfig.client.identity;
this.jsonAllConfig.bootstrap.lwm2mServer.clientSecretKey = this.jsonAllConfig.client.key;
this.lwm2mConfigFormGroup.get('lwm2mServer').patchValue(this.jsonAllConfig.bootstrap.lwm2mServer, {emitEvent: false});
}
}
private upDateJsonAllConfig = (): void => {
this.lwm2mConfigFormGroup.patchValue({
jsonAllConfig: this.jsonAllConfig
}, {emitEvent: false});
}
private initLwm2mConfigFormGroup = (): FormGroup => {
if (this.jsonAllConfig.client.securityConfigClientMode === Lwm2mSecurityType.PSK) {
this.data.endPoint = this.jsonAllConfig.client.endpoint;
}
const formGroup = this.fb.group({
securityConfigClientMode: [this.jsonAllConfig.client.securityConfigClientMode],
identity: [''],
key: [''],
x509: [false],
bootstrapServer: [this.jsonAllConfig.bootstrap.bootstrapServer],
lwm2mServer: [this.jsonAllConfig.bootstrap.lwm2mServer],
endPoint: [this.data.endPoint],
jsonAllConfig: [this.jsonAllConfig]
});
formGroup.get('securityConfigClientMode').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((type) => {
this.securityConfigClientModeChanged(type);
});
formGroup.get('endPoint').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((endpoint) => {
if (formGroup.get('securityConfigClientMode').value === Lwm2mSecurityType.PSK) {
this.jsonAllConfig.client.endpoint = endpoint;
this.upDateJsonAllConfig();
}
});
return formGroup;
}
save(): void {
this.upDateValueToJson();
this.data.endPoint = this.lwm2mConfigFormGroup.get('endPoint').value.split('\'').join('');
this.data.jsonAllConfig = this.jsonAllConfig;
if (this.lwm2mConfigFormGroup.get('securityConfigClientMode').value === Lwm2mSecurityType.PSK) {
this.data.endPoint = this.data.jsonAllConfig.client.identity;
}
this.dialogRef.close(this.data);
}
cancel(): void {
this.dialogRef.close(undefined);
}
}

3
ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.html

@ -39,7 +39,8 @@
</mat-form-field>
</ng-template>
<ng-template [ngSwitchCase]="'isError'">
<tb-checkbox formControlName="isError" [falseValue]="''">
<tb-checkbox formControlName="isError" [falseValue]="''"
(ngModelChange)="changeIsError($event)">
{{ 'event.has-error' | translate }}
</tb-checkbox>
</ng-template>

6
ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.ts

@ -105,5 +105,11 @@ export class EventFilterPanelComponent {
cancel() {
this.overlayRef.dispose();
}
changeIsError(value: boolean | string) {
if (this.conditionError && value === '') {
this.eventFilterFormGroup.get('error').reset('', {emitEvent: false});
}
}
}

3
ui-ngx/src/app/modules/home/components/event/event-table-config.ts

@ -288,7 +288,7 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
break;
case EventType.LC_EVENT:
this.filterColumns.push(
{key: 'method', title: 'event.event'},
{key: 'event', title: 'event.event'},
{key: 'status', title: 'event.status'},
{key: 'error', title: 'event.error'}
);
@ -356,6 +356,7 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
componentRef.onDestroy(() => {
if (componentRef.instance.result && !isEqual(this.filterParams, componentRef.instance.result.filterParams)) {
this.filterParams = componentRef.instance.result.filterParams;
this.table.paginator.pageIndex = 0;
this.table.updateData();
}
});

6
ui-ngx/src/app/modules/home/components/home-components.module.ts

@ -139,6 +139,8 @@ import { EMBED_DASHBOARD_DIALOG_TOKEN } from '@home/components/widget/dialog/emb
import { EdgeDownlinkTableComponent } from '@home/components/edge/edge-downlink-table.component';
import { EdgeDownlinkTableHeaderComponent } from '@home/components/edge/edge-downlink-table-header.component';
import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-page/widget-types-panel.component';
import { SecurityConfigLwm2mComponent } from '@home/components/device/security-config-lwm2m.component';
import { SecurityConfigLwm2mServerComponent } from '@home/components/device/security-config-lwm2m-server.component';
@NgModule({
declarations:
@ -241,6 +243,8 @@ import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-pag
DeviceWizardDialogComponent,
DeviceCredentialsComponent,
CopyDeviceCredentialsComponent,
SecurityConfigLwm2mComponent,
SecurityConfigLwm2mServerComponent,
AlarmScheduleDialogComponent,
EditAlarmDetailsDialogComponent,
SmsProviderConfigurationComponent,
@ -345,6 +349,8 @@ import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-pag
DeviceWizardDialogComponent,
DeviceCredentialsComponent,
CopyDeviceCredentialsComponent,
SecurityConfigLwm2mComponent,
SecurityConfigLwm2mServerComponent,
AlarmScheduleInfoComponent,
AlarmScheduleComponent,
AlarmScheduleDialogComponent,

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save