Browse Source

Merge branch 'master' of https://github.com/thingsboard/thingsboard into feature/persisted-rpc

# Conflicts:
#	common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java
pull/4722/head
YevhenBondarenko 5 years ago
parent
commit
5626c30f2f
  1. 160
      application/src/main/data/json/demo/dashboards/firmware.json
  2. 2492
      application/src/main/data/json/demo/dashboards/software.json
  3. 6
      application/src/main/data/json/system/widget_bundles/control_widgets.json
  4. 62
      application/src/main/data/upgrade/3.2.2/schema_update.sql
  5. 5
      application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
  6. 30
      application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java
  7. 27
      application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java
  8. 1
      application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
  9. 26
      application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java
  10. 8
      application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java
  11. 12
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java
  12. 8
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java
  13. 8
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java
  14. 8
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java
  15. 5
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java
  16. 16
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java
  17. 27
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java
  18. 22
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/TbOAuth2ParameterNames.java
  19. 2
      application/src/main/resources/thingsboard.yml
  20. 47
      application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java
  21. 69
      application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java
  22. 48
      application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java
  23. 121
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java
  24. 4
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SimpleLwM2MDevice.java
  25. BIN
      application/src/test/resources/lwm2m/credentials/clientKeyStore.jks
  26. BIN
      application/src/test/resources/lwm2m/credentials/serverKeyStore.jks
  27. 21
      common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java
  28. 33
      common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2DomainId.java
  29. 33
      common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2MobileId.java
  30. 33
      common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ParamsId.java
  31. 33
      common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2RegistrationId.java
  32. 4
      common/data/src/main/java/org/thingsboard/server/common/data/id/deprecated/OAuth2ClientRegistrationId.java
  33. 4
      common/data/src/main/java/org/thingsboard/server/common/data/id/deprecated/OAuth2ClientRegistrationInfoId.java
  34. 2
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java
  35. 42
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Domain.java
  36. 34
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2DomainInfo.java
  37. 31
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Info.java
  38. 42
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Mobile.java
  39. 34
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MobileInfo.java
  40. 40
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Params.java
  41. 39
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ParamsInfo.java
  42. 79
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Registration.java
  43. 4
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2RegistrationInfo.java
  44. 20
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/PlatformType.java
  45. 45
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/ClientRegistrationDto.java
  46. 4
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/DomainInfo.java
  47. 5
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/ExtendedOAuth2ClientRegistrationInfo.java
  48. 8
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientRegistration.java
  49. 6
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientRegistrationInfo.java
  50. 4
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientsDomainParams.java
  51. 4
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientsParams.java
  52. 2
      common/queue/src/main/proto/queue.proto
  53. 21
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
  54. 6
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java
  55. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java
  56. 16
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java
  57. 12
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java
  58. 31
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java
  59. 3
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MAuthorizer.java
  60. 75
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java
  61. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MSecurityInfo.java
  62. 92
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/AbstractLwM2mTransportResource.java
  63. 547
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java
  64. 23
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java
  65. 17
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java
  66. 25
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOtaConvert.java
  67. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java
  68. 158
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java
  69. 11
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportMsgHandler.java
  70. 125
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java
  71. 6
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java
  72. 238
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java
  73. 22
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2MClientState.java
  74. 31
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2MClientStateException.java
  75. 91
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java
  76. 18
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java
  77. 195
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java
  78. 30
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientProfile.java
  79. 8
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientRpcRequest.java
  80. 210
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java
  81. 27
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbEditableSecurityStore.java
  82. 130
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbInMemorySecurityStore.java
  83. 54
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisRegistrationStore.java
  84. 141
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisSecurityStore.java
  85. 82
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java
  86. 14
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mStoreFactory.java
  87. 25
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbSecurityStore.java
  88. 6
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java
  89. 16
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  90. 76
      dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2DomainEntity.java
  91. 72
      dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MobileEntity.java
  92. 65
      dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java
  93. 221
      dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationEntity.java
  94. 7
      dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/AbstractOAuth2ClientRegistrationInfoEntity.java
  95. 5
      dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/ExtendedOAuth2ClientRegistrationInfoEntity.java
  96. 9
      dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/OAuth2ClientRegistrationEntity.java
  97. 5
      dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/OAuth2ClientRegistrationInfoEntity.java
  98. 32
      dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java
  99. 28
      dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2DomainDao.java
  100. 28
      dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2MobileDao.java

160
application/src/main/data/json/demo/dashboards/firmware.json

@ -223,6 +223,27 @@
"funcBody": null, "funcBody": null,
"usePostProcessing": null, "usePostProcessing": null,
"postFuncBody": null "postFuncBody": null
},
{
"name": "fw_url",
"type": "attribute",
"label": "fw_url",
"color": "#e91e63",
"settings": {
"columnWidth": "0px",
"useCellStyleFunction": false,
"cellStyleFunction": "",
"useCellContentFunction": false,
"cellContentFunction": "",
"defaultColumnVisibility": "hidden",
"columnSelectionToDisplay": "disabled"
},
"_hash": 0.4204673738685043,
"units": null,
"decimals": null,
"funcBody": null,
"usePostProcessing": null,
"postFuncBody": null
} }
] ]
} }
@ -249,23 +270,23 @@
"icon": "edit", "icon": "edit",
"type": "customPretty", "type": "customPretty",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>", "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customCss": "", "customCss": "form {\n min-width: 300px !important;\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
"customResources": [], "customResources": [],
"id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5"
}, },
{ {
"name": "Download firware", "name": "Download firmware",
"icon": "file_download", "icon": "file_download",
"type": "custom", "type": "custom",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}",
"id": "12533058-42f6-e75f-620c-219c48d01ec0" "id": "12533058-42f6-e75f-620c-219c48d01ec0"
}, },
{ {
"name": "Copy checksum", "name": "Copy checksum/URL",
"icon": "content_copy", "icon": "content_copy",
"type": "custom", "type": "custom",
"customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n}", "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}",
"id": "09323079-7111-87f7-90d1-c62cd7d85dc7" "id": "09323079-7111-87f7-90d1-c62cd7d85dc7"
} }
] ]
@ -997,6 +1018,27 @@
"funcBody": null, "funcBody": null,
"usePostProcessing": null, "usePostProcessing": null,
"postFuncBody": null "postFuncBody": null
},
{
"name": "fw_url",
"type": "attribute",
"label": "fw_url",
"color": "#e91e63",
"settings": {
"columnWidth": "0px",
"useCellStyleFunction": false,
"cellStyleFunction": "",
"useCellContentFunction": false,
"cellContentFunction": "",
"defaultColumnVisibility": "hidden",
"columnSelectionToDisplay": "disabled"
},
"_hash": 0.4204673738685043,
"units": null,
"decimals": null,
"funcBody": null,
"usePostProcessing": null,
"postFuncBody": null
} }
] ]
} }
@ -1023,23 +1065,23 @@
"icon": "edit", "icon": "edit",
"type": "customPretty", "type": "customPretty",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>", "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customCss": "", "customCss": "form {\n min-width: 300px !important;\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
"customResources": [], "customResources": [],
"id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5"
}, },
{ {
"name": "Download firware", "name": "Download firmware",
"icon": "file_download", "icon": "file_download",
"type": "custom", "type": "custom",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}",
"id": "12533058-42f6-e75f-620c-219c48d01ec0" "id": "12533058-42f6-e75f-620c-219c48d01ec0"
}, },
{ {
"name": "Copy checksum", "name": "Copy checksum/URL",
"icon": "content_copy", "icon": "content_copy",
"type": "custom", "type": "custom",
"customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n}", "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}",
"id": "09323079-7111-87f7-90d1-c62cd7d85dc7" "id": "09323079-7111-87f7-90d1-c62cd7d85dc7"
} }
] ]
@ -1273,6 +1315,27 @@
"funcBody": null, "funcBody": null,
"usePostProcessing": null, "usePostProcessing": null,
"postFuncBody": null "postFuncBody": null
},
{
"name": "fw_url",
"type": "attribute",
"label": "fw_url",
"color": "#e91e63",
"settings": {
"columnWidth": "0px",
"useCellStyleFunction": false,
"cellStyleFunction": "",
"useCellContentFunction": false,
"cellContentFunction": "",
"defaultColumnVisibility": "hidden",
"columnSelectionToDisplay": "disabled"
},
"_hash": 0.4204673738685043,
"units": null,
"decimals": null,
"funcBody": null,
"usePostProcessing": null,
"postFuncBody": null
} }
] ]
} }
@ -1299,23 +1362,23 @@
"icon": "edit", "icon": "edit",
"type": "customPretty", "type": "customPretty",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>", "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customCss": "", "customCss": "form {\n min-width: 300px !important;\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
"customResources": [], "customResources": [],
"id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5"
}, },
{ {
"name": "Download firware", "name": "Download firmware",
"icon": "file_download", "icon": "file_download",
"type": "custom", "type": "custom",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}",
"id": "12533058-42f6-e75f-620c-219c48d01ec0" "id": "12533058-42f6-e75f-620c-219c48d01ec0"
}, },
{ {
"name": "Copy checksum", "name": "Copy checksum/URL",
"icon": "content_copy", "icon": "content_copy",
"type": "custom", "type": "custom",
"customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n}", "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}",
"id": "09323079-7111-87f7-90d1-c62cd7d85dc7" "id": "09323079-7111-87f7-90d1-c62cd7d85dc7"
} }
] ]
@ -1549,6 +1612,27 @@
"funcBody": null, "funcBody": null,
"usePostProcessing": null, "usePostProcessing": null,
"postFuncBody": null "postFuncBody": null
},
{
"name": "fw_url",
"type": "attribute",
"label": "fw_url",
"color": "#e91e63",
"settings": {
"columnWidth": "0px",
"useCellStyleFunction": false,
"cellStyleFunction": "",
"useCellContentFunction": false,
"cellContentFunction": "",
"defaultColumnVisibility": "hidden",
"columnSelectionToDisplay": "disabled"
},
"_hash": 0.4204673738685043,
"units": null,
"decimals": null,
"funcBody": null,
"usePostProcessing": null,
"postFuncBody": null
} }
] ]
} }
@ -1575,23 +1659,23 @@
"icon": "edit", "icon": "edit",
"type": "customPretty", "type": "customPretty",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>", "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customCss": "", "customCss": "form {\n min-width: 300px !important;\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
"customResources": [], "customResources": [],
"id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5"
}, },
{ {
"name": "Download firware", "name": "Download firmware",
"icon": "file_download", "icon": "file_download",
"type": "custom", "type": "custom",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}",
"id": "12533058-42f6-e75f-620c-219c48d01ec0" "id": "12533058-42f6-e75f-620c-219c48d01ec0"
}, },
{ {
"name": "Copy checksum", "name": "Copy checksum/URL",
"icon": "content_copy", "icon": "content_copy",
"type": "custom", "type": "custom",
"customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n}", "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}",
"id": "09323079-7111-87f7-90d1-c62cd7d85dc7" "id": "09323079-7111-87f7-90d1-c62cd7d85dc7"
} }
] ]
@ -1825,6 +1909,27 @@
"funcBody": null, "funcBody": null,
"usePostProcessing": null, "usePostProcessing": null,
"postFuncBody": null "postFuncBody": null
},
{
"name": "fw_url",
"type": "attribute",
"label": "fw_url",
"color": "#e91e63",
"settings": {
"columnWidth": "0px",
"useCellStyleFunction": false,
"cellStyleFunction": "",
"useCellContentFunction": false,
"cellContentFunction": "",
"defaultColumnVisibility": "hidden",
"columnSelectionToDisplay": "disabled"
},
"_hash": 0.4204673738685043,
"units": null,
"decimals": null,
"funcBody": null,
"usePostProcessing": null,
"postFuncBody": null
} }
] ]
} }
@ -1851,23 +1956,23 @@
"icon": "edit", "icon": "edit",
"type": "customPretty", "type": "customPretty",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>", "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customCss": "", "customCss": "form {\n min-width: 300px !important;\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
"customResources": [], "customResources": [],
"id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5"
}, },
{ {
"name": "Download firware", "name": "Download firmware",
"icon": "file_download", "icon": "file_download",
"type": "custom", "type": "custom",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}",
"id": "12533058-42f6-e75f-620c-219c48d01ec0" "id": "12533058-42f6-e75f-620c-219c48d01ec0"
}, },
{ {
"name": "Copy checksum", "name": "Copy checksum/URL",
"icon": "content_copy", "icon": "content_copy",
"type": "custom", "type": "custom",
"customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n}", "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}",
"id": "09323079-7111-87f7-90d1-c62cd7d85dc7" "id": "09323079-7111-87f7-90d1-c62cd7d85dc7"
} }
] ]
@ -1936,7 +2041,7 @@
} }
}, },
"device_firmware_history": { "device_firmware_history": {
"name": "Device Firmware history", "name": "Firmware history: ${entityName}",
"root": false, "root": false,
"layouts": { "layouts": {
"main": { "main": {
@ -2379,7 +2484,8 @@
"titleColor": "rgba(0,0,0,0.870588)", "titleColor": "rgba(0,0,0,0.870588)",
"showFilters": true, "showFilters": true,
"showDashboardLogo": false, "showDashboardLogo": false,
"dashboardLogoUrl": null "dashboardLogoUrl": null,
"showUpdateDashboardImage": false
} }
}, },
"name": "Firmware" "name": "Firmware"

2492
application/src/main/data/json/demo/dashboards/software.json

File diff suppressed because it is too large

6
application/src/main/data/json/system/widget_bundles/control_widgets.json

File diff suppressed because one or more lines are too long

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

@ -79,6 +79,68 @@ CREATE TABLE IF NOT EXISTS ota_package (
CONSTRAINT ota_package_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) CONSTRAINT ota_package_tenant_title_version_unq_key UNIQUE (tenant_id, title, version)
); );
CREATE TABLE IF NOT EXISTS oauth2_params (
id uuid NOT NULL CONSTRAINT oauth2_params_pkey PRIMARY KEY,
enabled boolean,
tenant_id uuid,
created_time bigint NOT NULL
);
CREATE TABLE IF NOT EXISTS oauth2_registration (
id uuid NOT NULL CONSTRAINT oauth2_registration_pkey PRIMARY KEY,
oauth2_params_id uuid NOT NULL,
created_time bigint NOT NULL,
additional_info varchar,
client_id varchar(255),
client_secret varchar(255),
authorization_uri varchar(255),
token_uri varchar(255),
scope varchar(255),
platforms varchar(255),
user_info_uri varchar(255),
user_name_attribute_name varchar(255),
jwk_set_uri varchar(255),
client_authentication_method varchar(255),
login_button_label varchar(255),
login_button_icon varchar(255),
allow_user_creation boolean,
activate_user boolean,
type varchar(31),
basic_email_attribute_key varchar(31),
basic_first_name_attribute_key varchar(31),
basic_last_name_attribute_key varchar(31),
basic_tenant_name_strategy varchar(31),
basic_tenant_name_pattern varchar(255),
basic_customer_name_pattern varchar(255),
basic_default_dashboard_name varchar(255),
basic_always_full_screen boolean,
custom_url varchar(255),
custom_username varchar(255),
custom_password varchar(255),
custom_send_token boolean,
CONSTRAINT fk_registration_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS oauth2_domain (
id uuid NOT NULL CONSTRAINT oauth2_domain_pkey PRIMARY KEY,
oauth2_params_id uuid NOT NULL,
created_time bigint NOT NULL,
domain_name varchar(255),
domain_scheme varchar(31),
CONSTRAINT fk_domain_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE,
CONSTRAINT oauth2_domain_unq_key UNIQUE (oauth2_params_id, domain_name, domain_scheme)
);
CREATE TABLE IF NOT EXISTS oauth2_mobile (
id uuid NOT NULL CONSTRAINT oauth2_mobile_pkey PRIMARY KEY,
oauth2_params_id uuid NOT NULL,
created_time bigint NOT NULL,
pkg_name varchar(255),
callback_url_scheme varchar(255),
CONSTRAINT fk_mobile_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE,
CONSTRAINT oauth2_mobile_unq_key UNIQUE (oauth2_params_id, pkg_name)
);
ALTER TABLE dashboard ALTER TABLE dashboard
ADD COLUMN IF NOT EXISTS image varchar(1000000); ADD COLUMN IF NOT EXISTS image varchar(1000000);

5
application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java

@ -204,6 +204,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
syncSessionSet.add(key); syncSessionSet.add(key);
} }
}); });
log.trace("46) Rpc syncSessionSet [{}] subscription after sent [{}]",syncSessionSet, rpcSubscriptions);
syncSessionSet.forEach(rpcSubscriptions::remove); syncSessionSet.forEach(rpcSubscriptions::remove);
} }
@ -517,7 +518,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
} else { } else {
SessionInfoMetaData sessionMD = sessions.get(sessionId); SessionInfoMetaData sessionMD = sessions.get(sessionId);
if (sessionMD == null) { if (sessionMD == null) {
sessionMD = new SessionInfoMetaData(new SessionInfo(SessionType.SYNC, sessionInfo.getNodeId())); sessionMD = new SessionInfoMetaData(new SessionInfo(subscribeCmd.getSessionType(), sessionInfo.getNodeId()));
} }
sessionMD.setSubscribedToAttributes(true); sessionMD.setSubscribedToAttributes(true);
log.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId); log.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId);
@ -538,7 +539,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
} else { } else {
SessionInfoMetaData sessionMD = sessions.get(sessionId); SessionInfoMetaData sessionMD = sessions.get(sessionId);
if (sessionMD == null) { if (sessionMD == null) {
sessionMD = new SessionInfoMetaData(new SessionInfo(SessionType.SYNC, sessionInfo.getNodeId())); sessionMD = new SessionInfoMetaData(new SessionInfo(subscribeCmd.getSessionType(), sessionInfo.getNodeId()));
} }
sessionMD.setSubscribedToRPC(true); sessionMD.setSubscribedToRPC(true);
log.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId); log.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId);

30
application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java

@ -37,6 +37,8 @@ import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriComponentsBuilder;
import org.thingsboard.server.dao.oauth2.OAuth2Configuration; import org.thingsboard.server.dao.oauth2.OAuth2Configuration;
import org.thingsboard.server.dao.oauth2.OAuth2Service;
import org.thingsboard.server.service.security.auth.oauth2.TbOAuth2ParameterNames;
import org.thingsboard.server.utils.MiscUtils; import org.thingsboard.server.utils.MiscUtils;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -46,12 +48,13 @@ import java.security.NoSuchAlgorithmException;
import java.util.Base64; import java.util.Base64;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID;
@Service @Service
@Slf4j @Slf4j
public class CustomOAuth2AuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver { public class CustomOAuth2AuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver {
public static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization"; private static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization";
public static final String DEFAULT_LOGIN_PROCESSING_URI = "/login/oauth2/code/"; private static final String DEFAULT_LOGIN_PROCESSING_URI = "/login/oauth2/code/";
private static final String REGISTRATION_ID_URI_VARIABLE_NAME = "registrationId"; private static final String REGISTRATION_ID_URI_VARIABLE_NAME = "registrationId";
private static final char PATH_DELIMITER = '/'; private static final char PATH_DELIMITER = '/';
@ -63,6 +66,9 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza
@Autowired @Autowired
private ClientRegistrationRepository clientRegistrationRepository; private ClientRegistrationRepository clientRegistrationRepository;
@Autowired
private OAuth2Service oAuth2Service;
@Autowired(required = false) @Autowired(required = false)
private OAuth2Configuration oauth2Configuration; private OAuth2Configuration oauth2Configuration;
@ -71,7 +77,8 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza
public OAuth2AuthorizationRequest resolve(HttpServletRequest request) { public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
String registrationId = this.resolveRegistrationId(request); String registrationId = this.resolveRegistrationId(request);
String redirectUriAction = getAction(request, "login"); String redirectUriAction = getAction(request, "login");
return resolve(request, registrationId, redirectUriAction); String appPackage = getAppPackage(request);
return resolve(request, registrationId, redirectUriAction, appPackage);
} }
@Override @Override
@ -80,7 +87,8 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza
return null; return null;
} }
String redirectUriAction = getAction(request, "authorize"); String redirectUriAction = getAction(request, "authorize");
return resolve(request, registrationId, redirectUriAction); String appPackage = getAppPackage(request);
return resolve(request, registrationId, redirectUriAction, appPackage);
} }
private String getAction(HttpServletRequest request, String defaultAction) { private String getAction(HttpServletRequest request, String defaultAction) {
@ -91,8 +99,12 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza
return action; return action;
} }
private String getAppPackage(HttpServletRequest request) {
return request.getParameter("pkg");
}
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction) { private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction, String appPackage) {
if (registrationId == null) { if (registrationId == null) {
return null; return null;
} }
@ -104,6 +116,14 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza
Map<String, Object> attributes = new HashMap<>(); Map<String, Object> attributes = new HashMap<>();
attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId()); attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId());
if (!StringUtils.isEmpty(appPackage)) {
String callbackUrlScheme = this.oAuth2Service.findCallbackUrlScheme(UUID.fromString(registrationId), appPackage);
if (StringUtils.isEmpty(callbackUrlScheme)) {
throw new IllegalArgumentException("Invalid package: " + appPackage + ". No package info found for Client Registration.");
} else {
attributes.put(TbOAuth2ParameterNames.CALLBACK_URL_SCHEME, callbackUrlScheme);
}
}
OAuth2AuthorizationRequest.Builder builder; OAuth2AuthorizationRequest.Builder builder;
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) { if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) {

27
application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java

@ -22,12 +22,15 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; import org.thingsboard.server.common.data.oauth2.OAuth2Info;
import org.thingsboard.server.common.data.oauth2.PlatformType;
import org.thingsboard.server.dao.oauth2.OAuth2Configuration; import org.thingsboard.server.dao.oauth2.OAuth2Configuration;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Operation;
@ -49,7 +52,9 @@ public class OAuth2Controller extends BaseController {
@RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST) @RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST)
@ResponseBody @ResponseBody
public List<OAuth2ClientInfo> getOAuth2Clients(HttpServletRequest request) throws ThingsboardException { public List<OAuth2ClientInfo> getOAuth2Clients(HttpServletRequest request,
@RequestParam(required = false) String pkgName,
@RequestParam(required = false) String platform) throws ThingsboardException {
try { try {
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug("Executing getOAuth2Clients: [{}][{}][{}]", request.getScheme(), request.getServerName(), request.getServerPort()); log.debug("Executing getOAuth2Clients: [{}][{}][{}]", request.getScheme(), request.getServerName(), request.getServerPort());
@ -59,7 +64,13 @@ public class OAuth2Controller extends BaseController {
log.debug("Header: {} {}", header, request.getHeader(header)); log.debug("Header: {} {}", header, request.getHeader(header));
} }
} }
return oAuth2Service.getOAuth2Clients(MiscUtils.getScheme(request), MiscUtils.getDomainNameAndPort(request)); PlatformType platformType = null;
if (StringUtils.isNotEmpty(platform)) {
try {
platformType = PlatformType.valueOf(platform);
} catch (Exception e) {}
}
return oAuth2Service.getOAuth2Clients(MiscUtils.getScheme(request), MiscUtils.getDomainNameAndPort(request), pkgName, platformType);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -68,10 +79,10 @@ public class OAuth2Controller extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@RequestMapping(value = "/oauth2/config", method = RequestMethod.GET, produces = "application/json") @RequestMapping(value = "/oauth2/config", method = RequestMethod.GET, produces = "application/json")
@ResponseBody @ResponseBody
public OAuth2ClientsParams getCurrentOAuth2Params() throws ThingsboardException { public OAuth2Info getCurrentOAuth2Info() throws ThingsboardException {
try { try {
accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ); accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ);
return oAuth2Service.findOAuth2Params(); return oAuth2Service.findOAuth2Info();
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -80,11 +91,11 @@ public class OAuth2Controller extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@RequestMapping(value = "/oauth2/config", method = RequestMethod.POST) @RequestMapping(value = "/oauth2/config", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK) @ResponseStatus(value = HttpStatus.OK)
public OAuth2ClientsParams saveOAuth2Params(@RequestBody OAuth2ClientsParams oauth2Params) throws ThingsboardException { public OAuth2Info saveOAuth2Info(@RequestBody OAuth2Info oauth2Info) throws ThingsboardException {
try { try {
accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.WRITE); accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.WRITE);
oAuth2Service.saveOAuth2Params(oauth2Params); oAuth2Service.saveOAuth2Info(oauth2Info);
return oAuth2Service.findOAuth2Params(); return oAuth2Service.findOAuth2Info();
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }

1
application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java

@ -199,6 +199,7 @@ public class ThingsboardInstallService {
databaseEntitiesUpgradeService.upgradeDatabase("3.2.2"); databaseEntitiesUpgradeService.upgradeDatabase("3.2.2");
dataUpdateService.updateData("3.2.2"); dataUpdateService.updateData("3.2.2");
systemDataLoaderService.createOAuth2Templates();
log.info("Updating system data..."); log.info("Updating system data...");
systemDataLoaderService.updateSystemWidgets(); systemDataLoaderService.updateSystemWidgets();

26
application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java

@ -23,6 +23,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile; import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.profile.TbDeviceProfileNode; import org.thingsboard.rule.engine.profile.TbDeviceProfileNode;
import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration; import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration;
import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityView;
@ -35,6 +36,8 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
import org.thingsboard.server.common.data.kv.ReadTsKvQuery; import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.oauth2.OAuth2Info;
import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientsParams;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.TimePageLink;
@ -45,10 +48,11 @@ import org.thingsboard.server.dao.alarm.AlarmDao;
import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.entity.EntityService;
import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.oauth2.OAuth2Service;
import org.thingsboard.server.dao.oauth2.OAuth2Utils;
import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.service.install.InstallScripts; import org.thingsboard.server.service.install.InstallScripts;
import java.util.ArrayList; import java.util.ArrayList;
@ -88,6 +92,9 @@ public class DefaultDataUpdateService implements DataUpdateService {
@Autowired @Autowired
private AlarmDao alarmDao; private AlarmDao alarmDao;
@Autowired
private OAuth2Service oAuth2Service;
@Override @Override
public void updateData(String fromVersion) throws Exception { public void updateData(String fromVersion) throws Exception {
switch (fromVersion) { switch (fromVersion) {
@ -107,6 +114,7 @@ public class DefaultDataUpdateService implements DataUpdateService {
log.info("Updating data from version 3.2.2 to 3.3.0 ..."); log.info("Updating data from version 3.2.2 to 3.3.0 ...");
tenantsDefaultEdgeRuleChainUpdater.updateEntities(null); tenantsDefaultEdgeRuleChainUpdater.updateEntities(null);
tenantsAlarmsCustomerUpdater.updateEntities(null); tenantsAlarmsCustomerUpdater.updateEntities(null);
updateOAuth2Params();
break; break;
default: default:
throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion); throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion);
@ -362,4 +370,20 @@ public class DefaultDataUpdateService implements DataUpdateService {
} }
} }
private void updateOAuth2Params() {
try {
OAuth2ClientsParams oauth2ClientsParams = oAuth2Service.findOAuth2Params();
if (!oauth2ClientsParams.getDomainsParams().isEmpty()) {
log.info("Updating OAuth2 parameters ...");
OAuth2Info oAuth2Info = OAuth2Utils.clientParamsToOAuth2Info(oauth2ClientsParams);
oAuth2Service.saveOAuth2Info(oAuth2Info);
oAuth2Service.saveOAuth2Params(new OAuth2ClientsParams(false, Collections.emptyList()));
log.info("Successfully updated OAuth2 parameters!");
}
}
catch (Exception e) {
log.error("Failed to update OAuth2 parameters", e);
}
}
} }

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

@ -72,15 +72,15 @@ public class DefaultTbResourceService implements TbResourceService {
if (ResourceType.LWM2M_MODEL.equals(resource.getResourceType())) { if (ResourceType.LWM2M_MODEL.equals(resource.getResourceType())) {
try { try {
List<ObjectModel> objectModels = List<ObjectModel> objectModels =
ddfFileParser.parseEx(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText()); ddfFileParser.parse(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText());
if (!objectModels.isEmpty()) { if (!objectModels.isEmpty()) {
ObjectModel objectModel = objectModels.get(0); ObjectModel objectModel = objectModels.get(0);
String resourceKey = objectModel.id + LWM2M_SEPARATOR_KEY + objectModel.getVersion(); String resourceKey = objectModel.id + LWM2M_SEPARATOR_KEY + objectModel.version;
String name = objectModel.name; String name = objectModel.name;
resource.setResourceKey(resourceKey); resource.setResourceKey(resourceKey);
if (resource.getId() == null) { if (resource.getId() == null) {
resource.setTitle(name + " id=" + objectModel.id + " v" + objectModel.getVersion()); resource.setTitle(name + " id=" + objectModel.id + " v" + objectModel.version);
} }
resource.setSearchText(resourceKey + LWM2M_SEPARATOR_SEARCH_TEXT + name); resource.setSearchText(resourceKey + LWM2M_SEPARATOR_SEARCH_TEXT + name);
} else { } else {
@ -176,7 +176,7 @@ public class DefaultTbResourceService implements TbResourceService {
try { try {
DDFFileParser ddfFileParser = new DDFFileParser(new DefaultDDFFileValidator()); DDFFileParser ddfFileParser = new DDFFileParser(new DefaultDDFFileValidator());
List<ObjectModel> objectModels = List<ObjectModel> objectModels =
ddfFileParser.parseEx(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText()); ddfFileParser.parse(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText());
if (objectModels.size() == 0) { if (objectModels.size() == 0) {
return null; return null;
} else { } else {

12
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java

@ -33,8 +33,8 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.IdBased; import org.thingsboard.server.common.data.id.IdBased;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
import org.thingsboard.server.common.data.oauth2.OAuth2Registration;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
@ -93,9 +93,9 @@ public abstract class AbstractOAuth2ClientMapper {
private final Lock userCreationLock = new ReentrantLock(); private final Lock userCreationLock = new ReentrantLock();
protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, OAuth2ClientRegistrationInfo clientRegistration) { protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, OAuth2Registration registration) {
OAuth2MapperConfig config = clientRegistration.getMapperConfig(); OAuth2MapperConfig config = registration.getMapperConfig();
UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, oauth2User.getEmail()); UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, oauth2User.getEmail());
@ -139,9 +139,9 @@ public abstract class AbstractOAuth2ClientMapper {
} }
} }
if (clientRegistration.getAdditionalInfo() != null && if (registration.getAdditionalInfo() != null &&
clientRegistration.getAdditionalInfo().has("providerName")) { registration.getAdditionalInfo().has("providerName")) {
additionalInfo.put("authProviderName", clientRegistration.getAdditionalInfo().get("providerName").asText()); additionalInfo.put("authProviderName", registration.getAdditionalInfo().get("providerName").asText());
} }
user.setAdditionalInfo(additionalInfo); user.setAdditionalInfo(additionalInfo);

8
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java

@ -18,8 +18,8 @@ package org.thingsboard.server.service.security.auth.oauth2;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
import org.thingsboard.server.common.data.oauth2.OAuth2Registration;
import org.thingsboard.server.dao.oauth2.OAuth2User; import org.thingsboard.server.dao.oauth2.OAuth2User;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
@ -30,12 +30,12 @@ import java.util.Map;
public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper { public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper {
@Override @Override
public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2ClientRegistrationInfo clientRegistration) { public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) {
OAuth2MapperConfig config = clientRegistration.getMapperConfig(); OAuth2MapperConfig config = registration.getMapperConfig();
Map<String, Object> attributes = token.getPrincipal().getAttributes(); Map<String, Object> attributes = token.getPrincipal().getAttributes();
String email = BasicMapperUtils.getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); String email = BasicMapperUtils.getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey());
OAuth2User oauth2User = BasicMapperUtils.getOAuth2User(email, attributes, config); OAuth2User oauth2User = BasicMapperUtils.getOAuth2User(email, attributes, config);
return getOrCreateSecurityUserFromOAuth2User(oauth2User, clientRegistration); return getOrCreateSecurityUserFromOAuth2User(oauth2User, registration);
} }
} }

8
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java

@ -23,9 +23,9 @@ import org.springframework.security.oauth2.client.authentication.OAuth2Authentic
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig;
import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
import org.thingsboard.server.common.data.oauth2.OAuth2Registration;
import org.thingsboard.server.dao.oauth2.OAuth2User; import org.thingsboard.server.dao.oauth2.OAuth2User;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
@ -39,10 +39,10 @@ public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme
private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder(); private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
@Override @Override
public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2ClientRegistrationInfo clientRegistration) { public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) {
OAuth2MapperConfig config = clientRegistration.getMapperConfig(); OAuth2MapperConfig config = registration.getMapperConfig();
OAuth2User oauth2User = getOAuth2User(token, providerAccessToken, config.getCustom()); OAuth2User oauth2User = getOAuth2User(token, providerAccessToken, config.getCustom());
return getOrCreateSecurityUserFromOAuth2User(oauth2User, clientRegistration); return getOrCreateSecurityUserFromOAuth2User(oauth2User, registration);
} }
private synchronized OAuth2User getOAuth2User(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2CustomMapperConfig custom) { private synchronized OAuth2User getOAuth2User(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2CustomMapperConfig custom) {

8
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java

@ -23,8 +23,8 @@ import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
import org.thingsboard.server.common.data.oauth2.OAuth2Registration;
import org.thingsboard.server.dao.oauth2.OAuth2Configuration; import org.thingsboard.server.dao.oauth2.OAuth2Configuration;
import org.thingsboard.server.dao.oauth2.OAuth2User; import org.thingsboard.server.dao.oauth2.OAuth2User;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
@ -46,13 +46,13 @@ public class GithubOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme
private OAuth2Configuration oAuth2Configuration; private OAuth2Configuration oAuth2Configuration;
@Override @Override
public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2ClientRegistrationInfo clientRegistration) { public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) {
OAuth2MapperConfig config = clientRegistration.getMapperConfig(); OAuth2MapperConfig config = registration.getMapperConfig();
Map<String, String> githubMapperConfig = oAuth2Configuration.getGithubMapper(); Map<String, String> githubMapperConfig = oAuth2Configuration.getGithubMapper();
String email = getEmail(githubMapperConfig.get(EMAIL_URL_KEY), providerAccessToken); String email = getEmail(githubMapperConfig.get(EMAIL_URL_KEY), providerAccessToken);
Map<String, Object> attributes = token.getPrincipal().getAttributes(); Map<String, Object> attributes = token.getPrincipal().getAttributes();
OAuth2User oAuth2User = BasicMapperUtils.getOAuth2User(email, attributes, config); OAuth2User oAuth2User = BasicMapperUtils.getOAuth2User(email, attributes, config);
return getOrCreateSecurityUserFromOAuth2User(oAuth2User, clientRegistration); return getOrCreateSecurityUserFromOAuth2User(oAuth2User, registration);
} }
private synchronized String getEmail(String emailUrl, String oauth2Token) { private synchronized String getEmail(String emailUrl, String oauth2Token) {

5
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java

@ -16,9 +16,10 @@
package org.thingsboard.server.service.security.auth.oauth2; package org.thingsboard.server.service.security.auth.oauth2;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; import org.thingsboard.server.common.data.oauth2.OAuth2Registration;
import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
public interface OAuth2ClientMapper { public interface OAuth2ClientMapper {
SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2ClientRegistrationInfo clientRegistration); SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration);
} }

16
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java

@ -18,8 +18,10 @@ package org.thingsboard.server.service.security.auth.oauth2;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
@ -51,9 +53,19 @@ public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationF
public void onAuthenticationFailure(HttpServletRequest request, public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response, AuthenticationException exception) HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException { throws IOException, ServletException {
String baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request); String baseUrl;
String errorPrefix;
OAuth2AuthorizationRequest authorizationRequest = httpCookieOAuth2AuthorizationRequestRepository.loadAuthorizationRequest(request);
String callbackUrlScheme = authorizationRequest.getAttribute(TbOAuth2ParameterNames.CALLBACK_URL_SCHEME);
if (!StringUtils.isEmpty(callbackUrlScheme)) {
baseUrl = callbackUrlScheme + ":";
errorPrefix = "/?error=";
} else {
baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request);
errorPrefix = "/login?loginError=";
}
httpCookieOAuth2AuthorizationRequestRepository.removeAuthorizationRequestCookies(request, response); httpCookieOAuth2AuthorizationRequestRepository.removeAuthorizationRequestCookies(request, response);
getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" + getRedirectStrategy().sendRedirect(request, response, baseUrl + errorPrefix +
URLEncoder.encode(exception.getMessage(), StandardCharsets.UTF_8.toString())); URLEncoder.encode(exception.getMessage(), StandardCharsets.UTF_8.toString()));
} }
} }

27
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java

@ -20,12 +20,14 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; import org.thingsboard.server.common.data.oauth2.OAuth2Registration;
import org.thingsboard.server.common.data.security.model.JwtToken; import org.thingsboard.server.common.data.security.model.JwtToken;
import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.dao.oauth2.OAuth2Service;
import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository;
@ -72,17 +74,24 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS
public void onAuthenticationSuccess(HttpServletRequest request, public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, HttpServletResponse response,
Authentication authentication) throws IOException { Authentication authentication) throws IOException {
String baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request); OAuth2AuthorizationRequest authorizationRequest = httpCookieOAuth2AuthorizationRequestRepository.loadAuthorizationRequest(request);
String callbackUrlScheme = authorizationRequest.getAttribute(TbOAuth2ParameterNames.CALLBACK_URL_SCHEME);
String baseUrl;
if (!StringUtils.isEmpty(callbackUrlScheme)) {
baseUrl = callbackUrlScheme + ":";
} else {
baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request);
}
try { try {
OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication; OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication;
OAuth2ClientRegistrationInfo clientRegistration = oAuth2Service.findClientRegistrationInfo(UUID.fromString(token.getAuthorizedClientRegistrationId())); OAuth2Registration registration = oAuth2Service.findRegistration(UUID.fromString(token.getAuthorizedClientRegistrationId()));
OAuth2AuthorizedClient oAuth2AuthorizedClient = oAuth2AuthorizedClientService.loadAuthorizedClient( OAuth2AuthorizedClient oAuth2AuthorizedClient = oAuth2AuthorizedClientService.loadAuthorizedClient(
token.getAuthorizedClientRegistrationId(), token.getAuthorizedClientRegistrationId(),
token.getPrincipal().getName()); token.getPrincipal().getName());
OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(clientRegistration.getMapperConfig().getType()); OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(registration.getMapperConfig().getType());
SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, oAuth2AuthorizedClient.getAccessToken().getTokenValue(), SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, oAuth2AuthorizedClient.getAccessToken().getTokenValue(),
clientRegistration); registration);
JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser);
JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser);
@ -91,7 +100,13 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS
getRedirectStrategy().sendRedirect(request, response, baseUrl + "/?accessToken=" + accessToken.getToken() + "&refreshToken=" + refreshToken.getToken()); getRedirectStrategy().sendRedirect(request, response, baseUrl + "/?accessToken=" + accessToken.getToken() + "&refreshToken=" + refreshToken.getToken());
} catch (Exception e) { } catch (Exception e) {
clearAuthenticationAttributes(request, response); clearAuthenticationAttributes(request, response);
getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" + String errorPrefix;
if (!StringUtils.isEmpty(callbackUrlScheme)) {
errorPrefix = "/?error=";
} else {
errorPrefix = "/login?loginError=";
}
getRedirectStrategy().sendRedirect(request, response, baseUrl + errorPrefix +
URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.toString())); URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.toString()));
} }
} }

22
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/TbOAuth2ParameterNames.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.service.security.auth.oauth2;
public interface TbOAuth2ParameterNames {
String CALLBACK_URL_SCHEME = "callback_url_scheme";
}

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

@ -656,7 +656,7 @@ transport:
bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}"
bind_port: "${LWM2M_BIND_PORT_BS:5687}" bind_port: "${LWM2M_BIND_PORT_BS:5687}"
security: security:
bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" bind_address: "${LWM2M_BIND_ADDRESS_SECURITY_BS:0.0.0.0}"
bind_port: "${LWM2M_BIND_PORT_SECURITY_BS:5688}" bind_port: "${LWM2M_BIND_PORT_SECURITY_BS:5688}"
# Only for RPK: Public & Private Key. If the keystore file is missing or not working # Only for RPK: Public & Private Key. If the keystore file is missing or not working
public_x: "${LWM2M_SERVER_PUBLIC_X_BS:5017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f91}" public_x: "${LWM2M_SERVER_PUBLIC_X_BS:5017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f91}"

47
application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java

@ -60,6 +60,53 @@ import java.util.concurrent.ScheduledExecutorService;
@DaoSqlTest @DaoSqlTest
public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
protected final String TRANSPORT_CONFIGURATION = "{\n" +
" \"type\": \"LWM2M\",\n" +
" \"observeAttr\": {\n" +
" \"keyName\": {\n" +
" \"/3_1.0/0/9\": \"batteryLevel\"\n" +
" },\n" +
" \"observe\": [],\n" +
" \"attribute\": [\n" +
" ],\n" +
" \"telemetry\": [\n" +
" \"/3_1.0/0/9\"\n" +
" ],\n" +
" \"attributeLwm2m\": {}\n" +
" },\n" +
" \"bootstrap\": {\n" +
" \"servers\": {\n" +
" \"binding\": \"U\",\n" +
" \"shortId\": 123,\n" +
" \"lifetime\": 300,\n" +
" \"notifIfDisabled\": true,\n" +
" \"defaultMinPeriod\": 1\n" +
" },\n" +
" \"lwm2mServer\": {\n" +
" \"host\": \"localhost\",\n" +
" \"port\": 5686,\n" +
" \"serverId\": 123,\n" +
" \"serverPublicKey\": \"\",\n" +
" \"bootstrapServerIs\": false,\n" +
" \"clientHoldOffTime\": 1,\n" +
" \"bootstrapServerAccountTimeout\": 0\n" +
" },\n" +
" \"bootstrapServer\": {\n" +
" \"host\": \"localhost\",\n" +
" \"port\": 5687,\n" +
" \"serverId\": 111,\n" +
" \"securityMode\": \"NO_SEC\",\n" +
" \"serverPublicKey\": \"\",\n" +
" \"bootstrapServerIs\": true,\n" +
" \"clientHoldOffTime\": 1,\n" +
" \"bootstrapServerAccountTimeout\": 0\n" +
" }\n" +
" },\n" +
" \"clientLwM2mSettings\": {\n" +
" \"clientOnlyObserveAfterConnect\": 1\n" +
" }\n" +
"}";
protected DeviceProfile deviceProfile; protected DeviceProfile deviceProfile;
protected ScheduledExecutorService executor; protected ScheduledExecutorService executor;
protected TbTestWebSocketClient wsClient; protected TbTestWebSocketClient wsClient;

69
application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java

@ -22,6 +22,7 @@ import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials;
import org.thingsboard.server.common.data.query.EntityData; import org.thingsboard.server.common.data.query.EntityData;
import org.thingsboard.server.common.data.query.EntityDataPageLink; import org.thingsboard.server.common.data.query.EntityDataPageLink;
import org.thingsboard.server.common.data.query.EntityDataQuery; import org.thingsboard.server.common.data.query.EntityDataQuery;
@ -36,7 +37,6 @@ import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd; import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd;
import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient; import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient;
import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials; import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -46,60 +46,13 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
protected final String TRANSPORT_CONFIGURATION = "{\n" + private final int PORT = 5685;
" \"type\": \"LWM2M\",\n" + private final Security SECURITY = noSec("coap://localhost:" + PORT, 123);
" \"observeAttr\": {\n" + private final NetworkConfig COAP_CONFIG = new NetworkConfig().setString("COAP_PORT", Integer.toString(PORT));
" \"keyName\": {\n" + private final String ENDPOINT = "deviceAEndpoint";
" \"/3_1.0/0/9\": \"batteryLevel\"\n" +
" },\n" +
" \"observe\": [],\n" +
" \"attribute\": [\n" +
" ],\n" +
" \"telemetry\": [\n" +
" \"/3_1.0/0/9\"\n" +
" ],\n" +
" \"attributeLwm2m\": {}\n" +
" },\n" +
" \"bootstrap\": {\n" +
" \"servers\": {\n" +
" \"binding\": \"UQ\",\n" +
" \"shortId\": 123,\n" +
" \"lifetime\": 300,\n" +
" \"notifIfDisabled\": true,\n" +
" \"defaultMinPeriod\": 1\n" +
" },\n" +
" \"lwm2mServer\": {\n" +
" \"host\": \"localhost\",\n" +
" \"port\": 5685,\n" +
" \"serverId\": 123,\n" +
" \"securityMode\": \"NO_SEC\",\n" +
" \"serverPublicKey\": \"\",\n" +
" \"bootstrapServerIs\": false,\n" +
" \"clientHoldOffTime\": 1,\n" +
" \"bootstrapServerAccountTimeout\": 0\n" +
" },\n" +
" \"bootstrapServer\": {\n" +
" \"host\": \"localhost\",\n" +
" \"port\": 5687,\n" +
" \"serverId\": 111,\n" +
" \"securityMode\": \"NO_SEC\",\n" +
" \"serverPublicKey\": \"\",\n" +
" \"bootstrapServerIs\": true,\n" +
" \"clientHoldOffTime\": 1,\n" +
" \"bootstrapServerAccountTimeout\": 0\n" +
" }\n" +
" },\n" +
" \"clientLwM2mSettings\": {\n" +
" \"clientOnlyObserveAfterConnect\": 1\n" +
" }\n" +
"}";
private final int port = 5685;
private final Security security = noSec("coap://localhost:" + port, 123);
private final NetworkConfig coapConfig = new NetworkConfig().setString("COAP_PORT", Integer.toString(port));
@NotNull @NotNull
private Device createDevice(String deviceAEndpoint) throws Exception { private Device createDevice() throws Exception {
Device device = new Device(); Device device = new Device();
device.setName("Device A"); device.setName("Device A");
device.setDeviceProfileId(deviceProfile.getId()); device.setDeviceProfileId(deviceProfile.getId());
@ -114,7 +67,7 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
LwM2MCredentials noSecCredentials = new LwM2MCredentials(); LwM2MCredentials noSecCredentials = new LwM2MCredentials();
NoSecClientCredentials clientCredentials = new NoSecClientCredentials(); NoSecClientCredentials clientCredentials = new NoSecClientCredentials();
clientCredentials.setEndpoint(deviceAEndpoint); clientCredentials.setEndpoint(ENDPOINT);
noSecCredentials.setClient(clientCredentials); noSecCredentials.setClient(clientCredentials);
deviceCredentials.setCredentialsValue(JacksonUtil.toString(noSecCredentials)); deviceCredentials.setCredentialsValue(JacksonUtil.toString(noSecCredentials));
doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk()); doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk());
@ -125,9 +78,7 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
public void testConnectAndObserveTelemetry() throws Exception { public void testConnectAndObserveTelemetry() throws Exception {
createDeviceProfile(TRANSPORT_CONFIGURATION); createDeviceProfile(TRANSPORT_CONFIGURATION);
String deviceAEndpoint = "deviceAEndpoint"; Device device = createDevice();
Device device = createDevice(deviceAEndpoint);
SingleEntityFilter sef = new SingleEntityFilter(); SingleEntityFilter sef = new SingleEntityFilter();
sef.setSingleEntity(device.getId()); sef.setSingleEntity(device.getId());
@ -144,8 +95,8 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
wsClient.waitForReply(); wsClient.waitForReply();
wsClient.registerWaitForUpdate(); wsClient.registerWaitForUpdate();
LwM2MTestClient client = new LwM2MTestClient(executor, deviceAEndpoint); LwM2MTestClient client = new LwM2MTestClient(executor, ENDPOINT);
client.init(security, coapConfig); client.init(SECURITY, COAP_CONFIG);
String msg = wsClient.waitForUpdate(); String msg = wsClient.waitForUpdate();
EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);

48
application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java

@ -47,54 +47,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
protected final String TRANSPORT_CONFIGURATION = "{\n" +
" \"type\": \"LWM2M\",\n" +
" \"observeAttr\": {\n" +
" \"keyName\": {\n" +
" \"/3_1.0/0/9\": \"batteryLevel\"\n" +
" },\n" +
" \"observe\": [],\n" +
" \"attribute\": [\n" +
" ],\n" +
" \"telemetry\": [\n" +
" \"/3_1.0/0/9\"\n" +
" ],\n" +
" \"attributeLwm2m\": {}\n" +
" },\n" +
" \"bootstrap\": {\n" +
" \"servers\": {\n" +
" \"binding\": \"UQ\",\n" +
" \"shortId\": 123,\n" +
" \"lifetime\": 300,\n" +
" \"notifIfDisabled\": true,\n" +
" \"defaultMinPeriod\": 1\n" +
" },\n" +
" \"lwm2mServer\": {\n" +
" \"host\": \"localhost\",\n" +
" \"port\": 5686,\n" +
" \"serverId\": 123,\n" +
" \"serverPublicKey\": \"\",\n" +
" \"bootstrapServerIs\": false,\n" +
" \"clientHoldOffTime\": 1,\n" +
" \"bootstrapServerAccountTimeout\": 0\n" +
" },\n" +
" \"bootstrapServer\": {\n" +
" \"host\": \"localhost\",\n" +
" \"port\": 5687,\n" +
" \"serverId\": 111,\n" +
" \"securityMode\": \"NO_SEC\",\n" +
" \"serverPublicKey\": \"\",\n" +
" \"bootstrapServerIs\": true,\n" +
" \"clientHoldOffTime\": 1,\n" +
" \"bootstrapServerAccountTimeout\": 0\n" +
" }\n" +
" },\n" +
" \"clientLwM2mSettings\": {\n" +
" \"clientOnlyObserveAfterConnect\": 1\n" +
" }\n" +
"}";
private final int port = 5686; private final int port = 5686;
private final NetworkConfig coapConfig = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(port)); private final NetworkConfig coapConfig = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(port));
private final String endpoint = "deviceAEndpoint"; private final String endpoint = "deviceAEndpoint";

121
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java

@ -17,40 +17,37 @@ package org.thingsboard.server.transport.lwm2m.client;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.network.CoapEndpoint;
import org.eclipse.californium.core.network.config.NetworkConfig; import org.eclipse.californium.core.network.config.NetworkConfig;
import org.eclipse.californium.elements.Connector; import org.eclipse.californium.core.observe.ObservationStore;
import org.eclipse.californium.scandium.DTLSConnector; import org.eclipse.californium.scandium.DTLSConnector;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.californium.scandium.dtls.ClientHandshaker;
import org.eclipse.californium.scandium.dtls.DTLSSession;
import org.eclipse.californium.scandium.dtls.HandshakeException;
import org.eclipse.californium.scandium.dtls.Handshaker;
import org.eclipse.californium.scandium.dtls.ResumingClientHandshaker;
import org.eclipse.californium.scandium.dtls.ResumingServerHandshaker;
import org.eclipse.californium.scandium.dtls.ServerHandshaker;
import org.eclipse.californium.scandium.dtls.SessionAdapter;
import org.eclipse.leshan.client.californium.LeshanClient; import org.eclipse.leshan.client.californium.LeshanClient;
import org.eclipse.leshan.client.californium.LeshanClientBuilder; import org.eclipse.leshan.client.californium.LeshanClientBuilder;
import org.eclipse.leshan.client.engine.DefaultRegistrationEngineFactory; import org.eclipse.leshan.client.engine.DefaultRegistrationEngineFactory;
import org.eclipse.leshan.client.object.Security; import org.eclipse.leshan.client.object.Security;
import org.eclipse.leshan.client.object.Server; import org.eclipse.leshan.client.object.Server;
import org.eclipse.leshan.client.observer.LwM2mClientObserver; import org.eclipse.leshan.client.observer.LwM2mClientObserver;
import org.eclipse.leshan.client.resource.DummyInstanceEnabler;
import org.eclipse.leshan.client.resource.ObjectsInitializer; import org.eclipse.leshan.client.resource.ObjectsInitializer;
import org.eclipse.leshan.client.servers.ServerIdentity; import org.eclipse.leshan.client.servers.ServerIdentity;
import org.eclipse.leshan.core.LwM2mId;
import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.ResponseCode;
import org.eclipse.leshan.core.californium.DefaultEndpointFactory; import org.eclipse.leshan.core.californium.EndpointFactory;
import org.eclipse.leshan.core.model.InvalidDDFFileException;
import org.eclipse.leshan.core.model.LwM2mModel; import org.eclipse.leshan.core.model.LwM2mModel;
import org.eclipse.leshan.core.model.ObjectLoader; import org.eclipse.leshan.core.model.ObjectLoader;
import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.model.StaticModel; import org.eclipse.leshan.core.model.StaticModel;
import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder; import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder; import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder;
import org.eclipse.leshan.core.request.BindingMode;
import org.eclipse.leshan.core.request.BootstrapRequest; import org.eclipse.leshan.core.request.BootstrapRequest;
import org.eclipse.leshan.core.request.DeregisterRequest; import org.eclipse.leshan.core.request.DeregisterRequest;
import org.eclipse.leshan.core.request.RegisterRequest; import org.eclipse.leshan.core.request.RegisterRequest;
import org.eclipse.leshan.core.request.UpdateRequest; import org.eclipse.leshan.core.request.UpdateRequest;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
@ -67,7 +64,7 @@ public class LwM2MTestClient {
private final String endpoint; private final String endpoint;
private LeshanClient client; private LeshanClient client;
public void init(Security security, NetworkConfig coapConfig) { public void init(Security security, NetworkConfig coapConfig) throws InvalidDDFFileException, IOException {
String[] resources = new String[]{"0.xml", "1.xml", "2.xml", "3.xml"}; String[] resources = new String[]{"0.xml", "1.xml", "2.xml", "3.xml"};
List<ObjectModel> models = new ArrayList<>(); List<ObjectModel> models = new ArrayList<>();
for (String resourceName : resources) { for (String resourceName : resources) {
@ -76,82 +73,51 @@ public class LwM2MTestClient {
LwM2mModel model = new StaticModel(models); LwM2mModel model = new StaticModel(models);
ObjectsInitializer initializer = new ObjectsInitializer(model); ObjectsInitializer initializer = new ObjectsInitializer(model);
initializer.setInstancesForObject(SECURITY, security); initializer.setInstancesForObject(SECURITY, security);
initializer.setInstancesForObject(SERVER, new Server(123, 300, BindingMode.U, false)); initializer.setInstancesForObject(SERVER, new Server(123, 300));
initializer.setInstancesForObject(DEVICE, new SimpleLwM2MDevice()); initializer.setInstancesForObject(DEVICE, new SimpleLwM2MDevice());
initializer.setClassForObject(LwM2mId.ACCESS_CONTROL, DummyInstanceEnabler.class);
DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder();
dtlsConfig.setRecommendedCipherSuitesOnly(true); dtlsConfig.setRecommendedCipherSuitesOnly(true);
dtlsConfig.setClientOnly();
DefaultRegistrationEngineFactory engineFactory = new DefaultRegistrationEngineFactory(); DefaultRegistrationEngineFactory engineFactory = new DefaultRegistrationEngineFactory();
engineFactory.setReconnectOnUpdate(false); engineFactory.setReconnectOnUpdate(false);
engineFactory.setResumeOnConnect(true); engineFactory.setResumeOnConnect(true);
DefaultEndpointFactory endpointFactory = new DefaultEndpointFactory(endpoint) { EndpointFactory endpointFactory = new EndpointFactory() {
@Override @Override
protected Connector createSecuredConnector(DtlsConnectorConfig dtlsConfig) { public CoapEndpoint createUnsecuredEndpoint(InetSocketAddress address, NetworkConfig coapConfig,
ObservationStore store) {
return new DTLSConnector(dtlsConfig) { CoapEndpoint.Builder builder = new CoapEndpoint.Builder();
@Override builder.setInetSocketAddress(address);
protected void onInitializeHandshaker(Handshaker handshaker) { builder.setNetworkConfig(coapConfig);
handshaker.addSessionListener(new SessionAdapter() { return builder.build();
}
@Override
public void handshakeStarted(Handshaker handshaker) throws HandshakeException { @Override
if (handshaker instanceof ServerHandshaker) { public CoapEndpoint createSecuredEndpoint(DtlsConnectorConfig dtlsConfig, NetworkConfig coapConfig,
log.info("DTLS Full Handshake initiated by server : STARTED ..."); ObservationStore store) {
} else if (handshaker instanceof ResumingServerHandshaker) { CoapEndpoint.Builder builder = new CoapEndpoint.Builder();
log.info("DTLS abbreviated Handshake initiated by server : STARTED ..."); DtlsConnectorConfig.Builder dtlsConfigBuilder = new DtlsConnectorConfig.Builder(dtlsConfig);
} else if (handshaker instanceof ClientHandshaker) {
log.info("DTLS Full Handshake initiated by client : STARTED ..."); // tricks to be able to change psk information on the fly
} else if (handshaker instanceof ResumingClientHandshaker) { // AdvancedPskStore pskStore = dtlsConfig.getAdvancedPskStore();
log.info("DTLS abbreviated Handshake initiated by client : STARTED ..."); // if (pskStore != null) {
} // PskPublicInformation identity = pskStore.getIdentity(null, null);
} // SecretKey key = pskStore
// .requestPskSecretResult(ConnectionId.EMPTY, null, identity, null, null, null).getSecret();
@Override // singlePSKStore = new SinglePSKStore(identity, key);
public void sessionEstablished(Handshaker handshaker, DTLSSession establishedSession) // dtlsConfigBuilder.setAdvancedPskStore(singlePSKStore);
throws HandshakeException { // }
if (handshaker instanceof ServerHandshaker) { builder.setConnector(new DTLSConnector(dtlsConfigBuilder.build()));
log.info("DTLS Full Handshake initiated by server : SUCCEED, handshaker {}", handshaker); builder.setNetworkConfig(coapConfig);
} else if (handshaker instanceof ResumingServerHandshaker) { return builder.build();
log.info("DTLS abbreviated Handshake initiated by server : SUCCEED, handshaker {}", handshaker);
} else if (handshaker instanceof ClientHandshaker) {
log.info("DTLS Full Handshake initiated by client : SUCCEED, handshaker {}", handshaker);
} else if (handshaker instanceof ResumingClientHandshaker) {
log.info("DTLS abbreviated Handshake initiated by client : SUCCEED, handshaker {}", handshaker);
}
}
@Override
public void handshakeFailed(Handshaker handshaker, Throwable error) {
/** get cause */
String cause;
if (error != null) {
if (error.getMessage() != null) {
cause = error.getMessage();
} else {
cause = error.getClass().getName();
}
} else {
cause = "unknown cause";
}
if (handshaker instanceof ServerHandshaker) {
log.info("DTLS Full Handshake initiated by server : FAILED [{}]", cause);
} else if (handshaker instanceof ResumingServerHandshaker) {
log.info("DTLS abbreviated Handshake initiated by server : FAILED [{}]", cause);
} else if (handshaker instanceof ClientHandshaker) {
log.info("DTLS Full Handshake initiated by client : FAILED [{}]", cause);
} else if (handshaker instanceof ResumingClientHandshaker) {
log.info("DTLS abbreviated Handshake initiated by client : FAILED [{}]", cause);
}
}
});
}
};
} }
}; };
LeshanClientBuilder builder = new LeshanClientBuilder(endpoint); LeshanClientBuilder builder = new LeshanClientBuilder(endpoint);
builder.setLocalAddress("0.0.0.0", 11000); builder.setLocalAddress("0.0.0.0", 11000);
builder.setObjects(initializer.createAll()); builder.setObjects(initializer.createAll());
@ -246,6 +212,11 @@ public class LwM2MTestClient {
public void onDeregistrationTimeout(ServerIdentity server, DeregisterRequest request) { public void onDeregistrationTimeout(ServerIdentity server, DeregisterRequest request) {
log.info("ClientObserver ->onDeregistrationTimeout... DeregisterRequest [{}] [{}]", request.getRegistrationId(), request.getRegistrationId()); log.info("ClientObserver ->onDeregistrationTimeout... DeregisterRequest [{}] [{}]", request.getRegistrationId(), request.getRegistrationId());
} }
@Override
public void onUnexpectedError(Throwable unexpectedError) {
}
}; };
this.client.addObserver(observer); this.client.addObserver(observer);

4
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SimpleLwM2MDevice.java

@ -97,7 +97,7 @@ public class SimpleLwM2MDevice extends BaseInstanceEnabler implements Destroyabl
} }
@Override @Override
public WriteResponse write(ServerIdentity identity, int resourceid, LwM2mResource value) { public WriteResponse write(ServerIdentity identity, boolean replace, int resourceid, LwM2mResource value) {
log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceid); log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceid);
switch (resourceid) { switch (resourceid) {
@ -112,7 +112,7 @@ public class SimpleLwM2MDevice extends BaseInstanceEnabler implements Destroyabl
fireResourcesChange(resourceid); fireResourcesChange(resourceid);
return WriteResponse.success(); return WriteResponse.success();
default: default:
return super.write(identity, resourceid, value); return super.write(identity, replace, resourceid, value);
} }
} }

BIN
application/src/test/resources/lwm2m/credentials/clientKeyStore.jks

Binary file not shown.

BIN
application/src/test/resources/lwm2m/credentials/serverKeyStore.jks

Binary file not shown.

21
common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java

@ -16,20 +16,31 @@
package org.thingsboard.server.dao.oauth2; package org.thingsboard.server.dao.oauth2;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; import org.thingsboard.server.common.data.oauth2.OAuth2Info;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; import org.thingsboard.server.common.data.oauth2.OAuth2Registration;
import org.thingsboard.server.common.data.oauth2.PlatformType;
import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo;
import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientsParams;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
public interface OAuth2Service { public interface OAuth2Service {
List<OAuth2ClientInfo> getOAuth2Clients(String domainScheme, String domainName); List<OAuth2ClientInfo> getOAuth2Clients(String domainScheme, String domainName, String pkgName, PlatformType platformType);
@Deprecated
void saveOAuth2Params(OAuth2ClientsParams oauth2Params); void saveOAuth2Params(OAuth2ClientsParams oauth2Params);
@Deprecated
OAuth2ClientsParams findOAuth2Params(); OAuth2ClientsParams findOAuth2Params();
OAuth2ClientRegistrationInfo findClientRegistrationInfo(UUID id); void saveOAuth2Info(OAuth2Info oauth2Info);
List<OAuth2ClientRegistrationInfo> findAllClientRegistrationInfos(); OAuth2Info findOAuth2Info();
OAuth2Registration findRegistration(UUID id);
List<OAuth2Registration> findAllRegistrations();
String findCallbackUrlScheme(UUID registrationId, String pkgName);
} }

33
common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2DomainId.java

@ -0,0 +1,33 @@
/**
* 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.id;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.UUID;
public class OAuth2DomainId extends UUIDBased {
@JsonCreator
public OAuth2DomainId(@JsonProperty("id") UUID id) {
super(id);
}
public static OAuth2DomainId fromString(String oauth2DomainId) {
return new OAuth2DomainId(UUID.fromString(oauth2DomainId));
}
}

33
common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2MobileId.java

@ -0,0 +1,33 @@
/**
* 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.id;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.UUID;
public class OAuth2MobileId extends UUIDBased {
@JsonCreator
public OAuth2MobileId(@JsonProperty("id") UUID id) {
super(id);
}
public static OAuth2MobileId fromString(String oauth2MobileId) {
return new OAuth2MobileId(UUID.fromString(oauth2MobileId));
}
}

33
common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ParamsId.java

@ -0,0 +1,33 @@
/**
* 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.id;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.UUID;
public class OAuth2ParamsId extends UUIDBased {
@JsonCreator
public OAuth2ParamsId(@JsonProperty("id") UUID id) {
super(id);
}
public static OAuth2ParamsId fromString(String oauth2ParamsId) {
return new OAuth2ParamsId(UUID.fromString(oauth2ParamsId));
}
}

33
common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2RegistrationId.java

@ -0,0 +1,33 @@
/**
* 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.id;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.UUID;
public class OAuth2RegistrationId extends UUIDBased {
@JsonCreator
public OAuth2RegistrationId(@JsonProperty("id") UUID id) {
super(id);
}
public static OAuth2RegistrationId fromString(String oauth2RegistrationId) {
return new OAuth2RegistrationId(UUID.fromString(oauth2RegistrationId));
}
}

4
common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationId.java → common/data/src/main/java/org/thingsboard/server/common/data/id/deprecated/OAuth2ClientRegistrationId.java

@ -13,13 +13,15 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.common.data.id; package org.thingsboard.server.common.data.id.deprecated;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import org.thingsboard.server.common.data.id.UUIDBased;
import java.util.UUID; import java.util.UUID;
@Deprecated
public class OAuth2ClientRegistrationId extends UUIDBased { public class OAuth2ClientRegistrationId extends UUIDBased {
@JsonCreator @JsonCreator

4
common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationInfoId.java → common/data/src/main/java/org/thingsboard/server/common/data/id/deprecated/OAuth2ClientRegistrationInfoId.java

@ -13,13 +13,15 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.common.data.id; package org.thingsboard.server.common.data.id.deprecated;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import org.thingsboard.server.common.data.id.UUIDBased;
import java.util.UUID; import java.util.UUID;
@Deprecated
public class OAuth2ClientRegistrationInfoId extends UUIDBased { public class OAuth2ClientRegistrationInfoId extends UUIDBased {
@JsonCreator @JsonCreator

2
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java

@ -20,10 +20,8 @@ import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo;
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.List; import java.util.List;

42
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Domain.java

@ -0,0 +1,42 @@
/**
* 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.oauth2;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.id.OAuth2DomainId;
import org.thingsboard.server.common.data.id.OAuth2ParamsId;
@EqualsAndHashCode(callSuper = true)
@Data
@ToString
@NoArgsConstructor
public class OAuth2Domain extends BaseData<OAuth2DomainId> {
private OAuth2ParamsId oauth2ParamsId;
private String domainName;
private SchemeType domainScheme;
public OAuth2Domain(OAuth2Domain domain) {
super(domain);
this.oauth2ParamsId = domain.oauth2ParamsId;
this.domainName = domain.domainName;
this.domainScheme = domain.domainScheme;
}
}

34
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2DomainInfo.java

@ -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.
*/
package org.thingsboard.server.common.data.oauth2;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
@EqualsAndHashCode
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class OAuth2DomainInfo {
private SchemeType scheme;
private String name;
}

31
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Info.java

@ -0,0 +1,31 @@
/**
* 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.oauth2;
import lombok.*;
import java.util.List;
@EqualsAndHashCode
@Data
@ToString
@Builder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
public class OAuth2Info {
private boolean enabled;
private List<OAuth2ParamsInfo> oauth2ParamsInfos;
}

42
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Mobile.java

@ -0,0 +1,42 @@
/**
* 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.oauth2;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.id.OAuth2MobileId;
import org.thingsboard.server.common.data.id.OAuth2ParamsId;
@EqualsAndHashCode(callSuper = true)
@Data
@ToString
@NoArgsConstructor
public class OAuth2Mobile extends BaseData<OAuth2MobileId> {
private OAuth2ParamsId oauth2ParamsId;
private String pkgName;
private String callbackUrlScheme;
public OAuth2Mobile(OAuth2Mobile mobile) {
super(mobile);
this.oauth2ParamsId = mobile.oauth2ParamsId;
this.pkgName = mobile.pkgName;
this.callbackUrlScheme = mobile.callbackUrlScheme;
}
}

34
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MobileInfo.java

@ -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.
*/
package org.thingsboard.server.common.data.oauth2;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
@EqualsAndHashCode
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class OAuth2MobileInfo {
private String pkgName;
private String callbackUrlScheme;
}

40
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Params.java

@ -0,0 +1,40 @@
/**
* 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.oauth2;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.id.OAuth2ParamsId;
import org.thingsboard.server.common.data.id.TenantId;
@EqualsAndHashCode(callSuper = true)
@Data
@ToString
@NoArgsConstructor
public class OAuth2Params extends BaseData<OAuth2ParamsId> {
private boolean enabled;
private TenantId tenantId;
public OAuth2Params(OAuth2Params oauth2Params) {
super(oauth2Params);
this.enabled = oauth2Params.enabled;
this.tenantId = oauth2Params.tenantId;
}
}

39
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ParamsInfo.java

@ -0,0 +1,39 @@
/**
* 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.oauth2;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.util.List;
@EqualsAndHashCode
@Data
@ToString
@Builder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
public class OAuth2ParamsInfo {
private List<OAuth2DomainInfo> domainInfos;
private List<OAuth2MobileInfo> mobileInfos;
private List<OAuth2RegistrationInfo> clientRegistrations;
}

79
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Registration.java

@ -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.
*/
package org.thingsboard.server.common.data.oauth2;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo;
import org.thingsboard.server.common.data.id.OAuth2ParamsId;
import org.thingsboard.server.common.data.id.OAuth2RegistrationId;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
@ToString(exclude = {"clientSecret"})
@NoArgsConstructor
public class OAuth2Registration extends SearchTextBasedWithAdditionalInfo<OAuth2RegistrationId> implements HasName {
private OAuth2ParamsId oauth2ParamsId;
private OAuth2MapperConfig mapperConfig;
private String clientId;
private String clientSecret;
private String authorizationUri;
private String accessTokenUri;
private List<String> scope;
private String userInfoUri;
private String userNameAttributeName;
private String jwkSetUri;
private String clientAuthenticationMethod;
private String loginButtonLabel;
private String loginButtonIcon;
private List<PlatformType> platforms;
public OAuth2Registration(OAuth2Registration registration) {
super(registration);
this.oauth2ParamsId = registration.oauth2ParamsId;
this.mapperConfig = registration.mapperConfig;
this.clientId = registration.clientId;
this.clientSecret = registration.clientSecret;
this.authorizationUri = registration.authorizationUri;
this.accessTokenUri = registration.accessTokenUri;
this.scope = registration.scope;
this.userInfoUri = registration.userInfoUri;
this.userNameAttributeName = registration.userNameAttributeName;
this.jwkSetUri = registration.jwkSetUri;
this.clientAuthenticationMethod = registration.clientAuthenticationMethod;
this.loginButtonLabel = registration.loginButtonLabel;
this.loginButtonIcon = registration.loginButtonIcon;
this.platforms = registration.platforms;
}
@Override
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
public String getName() {
return loginButtonLabel;
}
@Override
public String getSearchText() {
return getName();
}
}

4
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java → common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2RegistrationInfo.java

@ -17,7 +17,6 @@ package org.thingsboard.server.common.data.oauth2;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import lombok.*; import lombok.*;
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId;
import java.util.List; import java.util.List;
@ -27,7 +26,7 @@ import java.util.List;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@Builder @Builder
public class ClientRegistrationDto { public class OAuth2RegistrationInfo {
private OAuth2MapperConfig mapperConfig; private OAuth2MapperConfig mapperConfig;
private String clientId; private String clientId;
private String clientSecret; private String clientSecret;
@ -40,5 +39,6 @@ public class ClientRegistrationDto {
private String clientAuthenticationMethod; private String clientAuthenticationMethod;
private String loginButtonLabel; private String loginButtonLabel;
private String loginButtonIcon; private String loginButtonIcon;
private List<PlatformType> platforms;
private JsonNode additionalInfo; private JsonNode additionalInfo;
} }

20
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/PlatformType.java

@ -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.
*/
package org.thingsboard.server.common.data.oauth2;
public enum PlatformType {
WEB, ANDROID, IOS
}

45
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/ClientRegistrationDto.java

@ -0,0 +1,45 @@
/**
* 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.oauth2.deprecated;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.*;
import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
import java.util.List;
@Deprecated
@EqualsAndHashCode
@Data
@ToString(exclude = {"clientSecret"})
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ClientRegistrationDto {
private OAuth2MapperConfig mapperConfig;
private String clientId;
private String clientSecret;
private String authorizationUri;
private String accessTokenUri;
private List<String> scope;
private String userInfoUri;
private String userNameAttributeName;
private String jwkSetUri;
private String clientAuthenticationMethod;
private String loginButtonLabel;
private String loginButtonIcon;
private JsonNode additionalInfo;
}

4
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/DomainInfo.java → common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/DomainInfo.java

@ -13,10 +13,12 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.common.data.oauth2; package org.thingsboard.server.common.data.oauth2.deprecated;
import lombok.*; import lombok.*;
import org.thingsboard.server.common.data.oauth2.SchemeType;
@Deprecated
@EqualsAndHashCode @EqualsAndHashCode
@Data @Data
@ToString @ToString

5
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ExtendedOAuth2ClientRegistrationInfo.java → common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/ExtendedOAuth2ClientRegistrationInfo.java

@ -13,11 +13,14 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.common.data.oauth2; package org.thingsboard.server.common.data.oauth2.deprecated;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.oauth2.SchemeType;
import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo;
@Deprecated
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Data @Data
public class ExtendedOAuth2ClientRegistrationInfo extends OAuth2ClientRegistrationInfo { public class ExtendedOAuth2ClientRegistrationInfo extends OAuth2ClientRegistrationInfo {

8
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java → common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientRegistration.java

@ -13,16 +13,18 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.common.data.oauth2; package org.thingsboard.server.common.data.oauth2.deprecated;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationId;
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationInfoId;
import org.thingsboard.server.common.data.oauth2.SchemeType;
@Deprecated
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Data @Data
@ToString @ToString

6
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationInfo.java → common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientRegistrationInfo.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.common.data.oauth2; package org.thingsboard.server.common.data.oauth2.deprecated;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data; import lombok.Data;
@ -22,10 +22,12 @@ import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo;
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationInfoId;
import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
import java.util.List; import java.util.List;
@Deprecated
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Data @Data
@ToString(exclude = {"clientSecret"}) @ToString(exclude = {"clientSecret"})

4
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java → common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientsDomainParams.java

@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.common.data.oauth2; package org.thingsboard.server.common.data.oauth2.deprecated;
import lombok.*; import lombok.*;
import java.util.List; import java.util.List;
import java.util.Set;
@Deprecated
@EqualsAndHashCode @EqualsAndHashCode
@Data @Data
@ToString @ToString

4
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java → common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientsParams.java

@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.common.data.oauth2; package org.thingsboard.server.common.data.oauth2.deprecated;
import lombok.*; import lombok.*;
import java.util.List; import java.util.List;
import java.util.Set;
@Deprecated
@EqualsAndHashCode @EqualsAndHashCode
@Data @Data
@ToString @ToString

2
common/queue/src/main/proto/queue.proto

@ -310,10 +310,12 @@ message SessionCloseNotificationProto {
message SubscribeToAttributeUpdatesMsg { message SubscribeToAttributeUpdatesMsg {
bool unsubscribe = 1; bool unsubscribe = 1;
SessionType sessionType = 2;
} }
message SubscribeToRPCMsg { message SubscribeToRPCMsg {
bool unsubscribe = 1; bool unsubscribe = 1;
SessionType sessionType = 2;
} }
message ToDeviceRpcRequestMsg { message ToDeviceRpcRequestMsg {

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

@ -483,13 +483,16 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
String title = exchange.getQueryParameter("title"); String title = exchange.getQueryParameter("title");
String version = exchange.getQueryParameter("version"); String version = exchange.getQueryParameter("version");
if (msg.getResponseStatus().equals(TransportProtos.ResponseStatus.SUCCESS)) { if (msg.getResponseStatus().equals(TransportProtos.ResponseStatus.SUCCESS)) {
String firmwareId = new UUID(msg.getOtaPackageIdMSB(), msg.getOtaPackageIdLSB()).toString();
if (msg.getTitle().equals(title) && msg.getVersion().equals(version)) { if (msg.getTitle().equals(title) && msg.getVersion().equals(version)) {
String firmwareId = new UUID(msg.getOtaPackageIdMSB(), msg.getOtaPackageIdLSB()).toString();
String strChunkSize = exchange.getQueryParameter("size"); String strChunkSize = exchange.getQueryParameter("size");
String strChunk = exchange.getQueryParameter("chunk"); String strChunk = exchange.getQueryParameter("chunk");
int chunkSize = StringUtils.isEmpty(strChunkSize) ? 0 : Integer.parseInt(strChunkSize); int chunkSize = StringUtils.isEmpty(strChunkSize) ? 0 : Integer.parseInt(strChunkSize);
int chunk = StringUtils.isEmpty(strChunk) ? 0 : Integer.parseInt(strChunk); int chunk = StringUtils.isEmpty(strChunk) ? 0 : Integer.parseInt(strChunk);
exchange.respond(CoAP.ResponseCode.CONTENT, transportContext.getOtaPackageDataCache().get(firmwareId, chunkSize, chunk)); exchange.respond(CoAP.ResponseCode.CONTENT, transportContext.getOtaPackageDataCache().get(firmwareId, chunkSize, chunk));
}
else if (firmwareId != null) {
sendOtaData(exchange, firmwareId);
} else { } else {
exchange.respond(CoAP.ResponseCode.BAD_REQUEST); exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
} }
@ -505,6 +508,20 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
} }
} }
private void sendOtaData(CoapExchange exchange, String firmwareId) {
Response response = new Response(CoAP.ResponseCode.CONTENT);
byte[] fwData = transportContext.getOtaPackageDataCache().get(firmwareId);
if (fwData != null && fwData.length > 0) {
response.setPayload(fwData);
if (exchange.getRequestOptions().getBlock2() != null) {
int chunkSize = exchange.getRequestOptions().getBlock2().getSzx();
boolean moreFlag = fwData.length > chunkSize;
response.getOptions().setBlock2(chunkSize, moreFlag, 0);
}
exchange.respond(response);
}
}
private static class CoapSessionListener implements SessionMsgListener { private static class CoapSessionListener implements SessionMsgListener {
private final CoapTransportResource coapTransportResource; private final CoapTransportResource coapTransportResource;
@ -572,7 +589,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
} }
if (msg.getPersisted()) { if (msg.getPersisted()) {
RpcStatus status; RpcStatus status;
if (successful) { if (!successful) {
status = RpcStatus.FAILED; status = RpcStatus.FAILED;
} else if (msg.getOneway()) { } else if (msg.getOneway()) {
status = RpcStatus.SUCCESSFUL; status = RpcStatus.SUCCESSFUL;

6
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java

@ -102,15 +102,15 @@ public class LwM2MTransportBootstrapService {
builder.setLocalSecureAddress(bootstrapConfig.getSecureHost(), bootstrapConfig.getSecurePort()); builder.setLocalSecureAddress(bootstrapConfig.getSecureHost(), bootstrapConfig.getSecurePort());
/** Create CoAP Config */ /** Create CoAP Config */
builder.setCoapConfig(getCoapConfig(bootstrapConfig.getPort(), bootstrapConfig.getSecurePort())); builder.setCoapConfig(getCoapConfig(bootstrapConfig.getPort(), bootstrapConfig.getSecurePort(), serverConfig));
/** Define model provider (Create Models )*/ /** Define model provider (Create Models )*/
/** Create credentials */ /** Create credentials */
this.setServerWithCredentials(builder); this.setServerWithCredentials(builder);
/** Set securityStore with new ConfigStore */ // /** Set securityStore with new ConfigStore */
builder.setConfigStore(lwM2MInMemoryBootstrapConfigStore); // builder.setConfigStore(lwM2MInMemoryBootstrapConfigStore);
/** SecurityStore */ /** SecurityStore */
builder.setSecurityStore(lwM2MBootstrapSecurityStore); builder.setSecurityStore(lwM2MBootstrapSecurityStore);

2
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java

@ -69,7 +69,7 @@ public class LwM2MBootstrapConfig {
server0.lifetime = servers.getLifetime(); server0.lifetime = servers.getLifetime();
server0.defaultMinPeriod = servers.getDefaultMinPeriod(); server0.defaultMinPeriod = servers.getDefaultMinPeriod();
server0.notifIfDisabled = servers.isNotifIfDisabled(); server0.notifIfDisabled = servers.isNotifIfDisabled();
server0.binding = BindingMode.valueOf(servers.getBinding()); server0.binding = BindingMode.parse(servers.getBinding());
configBs.servers.put(0, server0); configBs.servers.put(0, server0);
/* Security Configuration (object 0) as defined in LWM2M 1.0.x TS. Bootstrap instance = 0 */ /* Security Configuration (object 0) as defined in LWM2M 1.0.x TS. Bootstrap instance = 0 */
this.bootstrapServer.setBootstrapServerIs(true); this.bootstrapServer.setBootstrapServerIs(true);

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

@ -30,7 +30,7 @@ import org.eclipse.leshan.server.security.SecurityInfo;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.transport.lwm2m.secure.EndpointSecurityInfo; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator; import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener; import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
@ -40,7 +40,7 @@ import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
import java.io.IOException; import java.io.IOException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.Iterator;
import java.util.UUID; import java.util.UUID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.BOOTSTRAP_SERVER; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.BOOTSTRAP_SERVER;
@ -71,8 +71,8 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
} }
@Override @Override
public List<SecurityInfo> getAllByEndpoint(String endPoint) { public Iterator<SecurityInfo> getAllByEndpoint(String endPoint) {
EndpointSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfo(endPoint, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP); TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(endPoint, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP);
if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) { if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) {
/* add value to store from BootstrapJson */ /* add value to store from BootstrapJson */
this.setBootstrapConfigScurityInfo(store); this.setBootstrapConfigScurityInfo(store);
@ -88,7 +88,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
} catch (InvalidConfigurationException e) { } catch (InvalidConfigurationException e) {
log.error("", e); log.error("", e);
} }
return store.getSecurityInfo() == null ? null : Collections.singletonList(store.getSecurityInfo()); return store.getSecurityInfo() == null ? null : Collections.singletonList(store.getSecurityInfo()).iterator();
} }
} }
return null; return null;
@ -96,7 +96,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
@Override @Override
public SecurityInfo getByIdentity(String identity) { public SecurityInfo getByIdentity(String identity) {
EndpointSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfo(identity, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP); TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(identity, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP);
if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) { if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) {
/* add value to store from BootstrapJson */ /* add value to store from BootstrapJson */
this.setBootstrapConfigScurityInfo(store); this.setBootstrapConfigScurityInfo(store);
@ -113,7 +113,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
return null; return null;
} }
private void setBootstrapConfigScurityInfo(EndpointSecurityInfo store) { private void setBootstrapConfigScurityInfo(TbLwM2MSecurityInfo store) {
/* BootstrapConfig */ /* BootstrapConfig */
LwM2MBootstrapConfig lwM2MBootstrapConfig = this.getParametersBootstrap(store); LwM2MBootstrapConfig lwM2MBootstrapConfig = this.getParametersBootstrap(store);
if (lwM2MBootstrapConfig != null) { if (lwM2MBootstrapConfig != null) {
@ -150,7 +150,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
} }
} }
private LwM2MBootstrapConfig getParametersBootstrap(EndpointSecurityInfo store) { private LwM2MBootstrapConfig getParametersBootstrap(TbLwM2MSecurityInfo store) {
try { try {
LwM2MBootstrapConfig lwM2MBootstrapConfig = store.getBootstrapCredentialConfig(); LwM2MBootstrapConfig lwM2MBootstrapConfig = store.getBootstrapCredentialConfig();
if (lwM2MBootstrapConfig != null) { if (lwM2MBootstrapConfig != null) {

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

@ -16,6 +16,7 @@
package org.thingsboard.server.transport.lwm2m.bootstrap.secure; package org.thingsboard.server.transport.lwm2m.bootstrap.secure;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.request.BootstrapRequest;
import org.eclipse.leshan.core.request.Identity; import org.eclipse.leshan.core.request.Identity;
import org.eclipse.leshan.server.bootstrap.BootstrapSession; import org.eclipse.leshan.server.bootstrap.BootstrapSession;
import org.eclipse.leshan.server.bootstrap.DefaultBootstrapSession; import org.eclipse.leshan.server.bootstrap.DefaultBootstrapSession;
@ -25,7 +26,7 @@ import org.eclipse.leshan.server.security.SecurityChecker;
import org.eclipse.leshan.server.security.SecurityInfo; import org.eclipse.leshan.server.security.SecurityInfo;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.Iterator;
@Slf4j @Slf4j
public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSessionManager { public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSessionManager {
@ -50,16 +51,17 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public BootstrapSession begin(String endpoint, Identity clientIdentity) { public BootstrapSession begin(BootstrapRequest request, Identity clientIdentity) {
boolean authorized; boolean authorized;
if (bsSecurityStore != null) { if (bsSecurityStore != null) {
List<SecurityInfo> securityInfos = (clientIdentity.getPskIdentity() != null && !clientIdentity.getPskIdentity().isEmpty()) ? Collections.singletonList(bsSecurityStore.getByIdentity(clientIdentity.getPskIdentity())) : bsSecurityStore.getAllByEndpoint(endpoint); Iterator<SecurityInfo> securityInfos = (clientIdentity.getPskIdentity() != null && !clientIdentity.getPskIdentity().isEmpty()) ?
Collections.singletonList(bsSecurityStore.getByIdentity(clientIdentity.getPskIdentity())).iterator() : bsSecurityStore.getAllByEndpoint(request.getEndpointName());
log.info("Bootstrap session started securityInfos: [{}]", securityInfos); log.info("Bootstrap session started securityInfos: [{}]", securityInfos);
authorized = securityChecker.checkSecurityInfos(endpoint, clientIdentity, securityInfos); authorized = securityChecker.checkSecurityInfos(request.getEndpointName(), clientIdentity, securityInfos);
} else { } else {
authorized = true; authorized = true;
} }
DefaultBootstrapSession session = new DefaultBootstrapSession(endpoint, clientIdentity, authorized); DefaultBootstrapSession session = new DefaultBootstrapSession(request, clientIdentity, authorized);
log.info("Bootstrap session started : {}", session); log.info("Bootstrap session started : {}", session);
return session; return session;
} }

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

@ -22,16 +22,16 @@ import org.eclipse.leshan.server.security.SecurityInfo;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode;
import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.RPKClientCredentials;
import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials;
import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials; import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.RPKClientCredentials;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
@ -55,15 +55,15 @@ public class LwM2mCredentialsSecurityInfoValidator {
private final LwM2mTransportContext context; private final LwM2mTransportContext context;
private final LwM2MTransportServerConfig config; private final LwM2MTransportServerConfig config;
public EndpointSecurityInfo getEndpointSecurityInfo(String endpoint, LwM2mTransportUtil.LwM2mTypeServer keyValue) { public TbLwM2MSecurityInfo getEndpointSecurityInfoByCredentialsId(String credentialsId, LwM2mTransportUtil.LwM2mTypeServer keyValue) {
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
final EndpointSecurityInfo[] resultSecurityStore = new EndpointSecurityInfo[1]; final TbLwM2MSecurityInfo[] resultSecurityStore = new TbLwM2MSecurityInfo[1];
context.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(endpoint).build(), context.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(credentialsId).build(),
new TransportServiceCallback<>() { new TransportServiceCallback<>() {
@Override @Override
public void onSuccess(ValidateDeviceCredentialsResponse msg) { public void onSuccess(ValidateDeviceCredentialsResponse msg) {
String credentialsBody = msg.getCredentials(); String credentialsBody = msg.getCredentials();
resultSecurityStore[0] = createSecurityInfo(endpoint, credentialsBody, keyValue); resultSecurityStore[0] = createSecurityInfo(credentialsId, credentialsBody, keyValue);
resultSecurityStore[0].setMsg(msg); resultSecurityStore[0].setMsg(msg);
resultSecurityStore[0].setDeviceProfile(msg.getDeviceProfile()); resultSecurityStore[0].setDeviceProfile(msg.getDeviceProfile());
latch.countDown(); latch.countDown();
@ -71,8 +71,8 @@ public class LwM2mCredentialsSecurityInfoValidator {
@Override @Override
public void onError(Throwable e) { public void onError(Throwable e) {
log.trace("[{}] [{}] Failed to process credentials ", endpoint, e); log.trace("[{}] [{}] Failed to process credentials ", credentialsId, e);
resultSecurityStore[0] = createSecurityInfo(endpoint, null, null); resultSecurityStore[0] = createSecurityInfo(credentialsId, null, null);
latch.countDown(); latch.countDown();
} }
}); });
@ -91,8 +91,8 @@ public class LwM2mCredentialsSecurityInfoValidator {
* @param keyValue - * @param keyValue -
* @return SecurityInfo * @return SecurityInfo
*/ */
private EndpointSecurityInfo createSecurityInfo(String endpoint, String jsonStr, LwM2mTransportUtil.LwM2mTypeServer keyValue) { private TbLwM2MSecurityInfo createSecurityInfo(String endpoint, String jsonStr, LwM2mTransportUtil.LwM2mTypeServer keyValue) {
EndpointSecurityInfo result = new EndpointSecurityInfo(); TbLwM2MSecurityInfo result = new TbLwM2MSecurityInfo();
LwM2MCredentials credentials = JacksonUtil.fromString(jsonStr, LwM2MCredentials.class); LwM2MCredentials credentials = JacksonUtil.fromString(jsonStr, LwM2MCredentials.class);
if (credentials != null) { if (credentials != null) {
if (keyValue.equals(LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP)) { if (keyValue.equals(LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP)) {
@ -104,6 +104,7 @@ public class LwM2mCredentialsSecurityInfoValidator {
result.setEndpoint(endpoint); result.setEndpoint(endpoint);
result.setSecurityMode(credentials.getBootstrap().getBootstrapServer().getSecurityMode()); result.setSecurityMode(credentials.getBootstrap().getBootstrapServer().getSecurityMode());
} else { } else {
result.setEndpoint(credentials.getClient().getEndpoint());
switch (credentials.getClient().getSecurityConfigClientMode()) { switch (credentials.getClient().getSecurityConfigClientMode()) {
case NO_SEC: case NO_SEC:
createClientSecurityInfoNoSec(result); createClientSecurityInfoNoSec(result);
@ -125,12 +126,12 @@ public class LwM2mCredentialsSecurityInfoValidator {
return result; return result;
} }
private void createClientSecurityInfoNoSec(EndpointSecurityInfo result) { private void createClientSecurityInfoNoSec(TbLwM2MSecurityInfo result) {
result.setSecurityInfo(null); result.setSecurityInfo(null);
result.setSecurityMode(NO_SEC); result.setSecurityMode(NO_SEC);
} }
private void createClientSecurityInfoPSK(EndpointSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) { private void createClientSecurityInfoPSK(TbLwM2MSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) {
PSKClientCredentials pskConfig = (PSKClientCredentials) clientCredentialsConfig; PSKClientCredentials pskConfig = (PSKClientCredentials) clientCredentialsConfig;
if (StringUtils.isNotEmpty(pskConfig.getIdentity())) { if (StringUtils.isNotEmpty(pskConfig.getIdentity())) {
try { try {
@ -149,7 +150,7 @@ public class LwM2mCredentialsSecurityInfoValidator {
} }
} }
private void createClientSecurityInfoRPK(EndpointSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) { private void createClientSecurityInfoRPK(TbLwM2MSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) {
RPKClientCredentials rpkConfig = (RPKClientCredentials) clientCredentialsConfig; RPKClientCredentials rpkConfig = (RPKClientCredentials) clientCredentialsConfig;
try { try {
if (rpkConfig.getKey() != null) { if (rpkConfig.getKey() != null) {
@ -164,7 +165,7 @@ public class LwM2mCredentialsSecurityInfoValidator {
} }
} }
private void createClientSecurityInfoX509(EndpointSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) { private void createClientSecurityInfoX509(TbLwM2MSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) {
result.setSecurityInfo(SecurityInfo.newX509CertInfo(endpoint)); result.setSecurityInfo(SecurityInfo.newX509CertInfo(endpoint));
result.setSecurityMode(X509); result.setSecurityMode(X509);
} }

3
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MAuthorizer.java

@ -27,6 +27,7 @@ import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2mSecurityStore; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2mSecurityStore;
import org.thingsboard.server.transport.lwm2m.server.store.TbSecurityStore;
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
@ -34,7 +35,7 @@ import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2mSecurityStore;
public class TbLwM2MAuthorizer implements Authorizer { public class TbLwM2MAuthorizer implements Authorizer {
private final TbLwM2MDtlsSessionStore sessionStorage; private final TbLwM2MDtlsSessionStore sessionStorage;
private final TbLwM2mSecurityStore securityStore; private final TbSecurityStore securityStore;
private final SecurityChecker securityChecker = new SecurityChecker(); private final SecurityChecker securityChecker = new SecurityChecker();
private final LwM2mClientContext clientContext; private final LwM2mClientContext clientContext;

75
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java

@ -29,22 +29,21 @@ import org.eclipse.californium.scandium.dtls.HandshakeResultHandler;
import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier; import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier;
import org.eclipse.californium.scandium.dtls.x509.StaticCertificateVerifier; import org.eclipse.californium.scandium.dtls.x509.StaticCertificateVerifier;
import org.eclipse.californium.scandium.util.ServerNames; import org.eclipse.californium.scandium.util.ServerNames;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode;
import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredentials;
import org.thingsboard.server.common.msg.EncryptionUtil; import org.thingsboard.server.common.msg.EncryptionUtil;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.common.transport.util.SslUtil; import org.thingsboard.server.common.transport.util.SslUtil;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials; import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredentials; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
import org.thingsboard.server.transport.lwm2m.server.store.TbEditableSecurityStore;
import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@ -57,8 +56,6 @@ import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@Slf4j @Slf4j
@Component @Component
@ -66,9 +63,10 @@ import java.util.concurrent.TimeUnit;
@RequiredArgsConstructor @RequiredArgsConstructor
public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVerifier { public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVerifier {
private final TransportService transportService;
private final TbLwM2MDtlsSessionStore sessionStorage; private final TbLwM2MDtlsSessionStore sessionStorage;
private final LwM2MTransportServerConfig config; private final LwM2MTransportServerConfig config;
private final LwM2mCredentialsSecurityInfoValidator securityInfoValidator;
private final TbEditableSecurityStore securityStore;
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
private StaticCertificateVerifier staticCertificateVerifier; private StaticCertificateVerifier staticCertificateVerifier;
@ -119,48 +117,33 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer
String strCert = SslUtil.getCertificateString(cert); String strCert = SslUtil.getCertificateString(cert);
String sha3Hash = EncryptionUtil.getSha3Hash(strCert); String sha3Hash = EncryptionUtil.getSha3Hash(strCert);
final ValidateDeviceCredentialsResponse[] deviceCredentialsResponse = new ValidateDeviceCredentialsResponse[1]; TbLwM2MSecurityInfo securityInfo = securityInfoValidator.getEndpointSecurityInfoByCredentialsId(sha3Hash, LwM2mTransportUtil.LwM2mTypeServer.CLIENT);
CountDownLatch latch = new CountDownLatch(1); ValidateDeviceCredentialsResponse msg = securityInfo != null ? securityInfo.getMsg() : null;
transportService.process(TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(sha3Hash).build(), if (msg != null && org.thingsboard.server.common.data.StringUtils.isNotEmpty(msg.getCredentials())) {
new TransportServiceCallback<>() { LwM2MCredentials credentials = JacksonUtil.fromString(msg.getCredentials(), LwM2MCredentials.class);
@Override if (!credentials.getClient().getSecurityConfigClientMode().equals(LwM2MSecurityMode.X509)) {
public void onSuccess(ValidateDeviceCredentialsResponse msg) { continue;
if (!StringUtils.isEmpty(msg.getCredentials())) { }
deviceCredentialsResponse[0] = msg; X509ClientCredentials config = (X509ClientCredentials) credentials.getClient();
} String certBody = config.getCert();
latch.countDown(); String endpoint = config.getEndpoint();
} if (strCert.equals(certBody)) {
x509CredentialsFound = true;
@Override DeviceProfile deviceProfile = msg.getDeviceProfile();
public void onError(Throwable e) { if (msg.hasDeviceInfo() && deviceProfile != null) {
log.error(e.getMessage(), e); sessionStorage.put(endpoint, new TbX509DtlsSessionInfo(cert.getSubjectX500Principal().getName(), msg));
latch.countDown(); try {
} securityStore.put(securityInfo);
}); } catch (NonUniqueSecurityInfoException e) {
if (latch.await(10, TimeUnit.SECONDS)) { log.trace("Failed to add security info: {}", securityInfo, e);
ValidateDeviceCredentialsResponse msg = deviceCredentialsResponse[0];
if (msg != null && org.thingsboard.server.common.data.StringUtils.isNotEmpty(msg.getCredentials())) {
LwM2MCredentials credentials = JacksonUtil.fromString(msg.getCredentials(), LwM2MCredentials.class);
if(!credentials.getClient().getSecurityConfigClientMode().equals(LwM2MSecurityMode.X509)){
continue;
}
X509ClientCredentials config = (X509ClientCredentials) credentials.getClient();
String certBody = config.getCert();
String endpoint = config.getEndpoint();
if (strCert.equals(certBody)) {
x509CredentialsFound = true;
DeviceProfile deviceProfile = msg.getDeviceProfile();
if (msg.hasDeviceInfo() && deviceProfile != null) {
sessionStorage.put(endpoint, new TbX509DtlsSessionInfo(cert.getSubjectX500Principal().getName(), msg));
break;
} }
} else { break;
log.trace("[{}][{}] Certificate mismatch. Expected: {}, Actual: {}", endpoint, sha3Hash, strCert, certBody);
} }
} else {
log.trace("[{}][{}] Certificate mismatch. Expected: {}, Actual: {}", endpoint, sha3Hash, strCert, certBody);
} }
} }
} catch (InterruptedException | } catch (CertificateEncodingException |
CertificateEncodingException |
CertificateExpiredException | CertificateExpiredException |
CertificateNotYetValidException e) { CertificateNotYetValidException e) {
log.error(e.getMessage(), e); log.error(e.getMessage(), e);

2
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/EndpointSecurityInfo.java → common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MSecurityInfo.java

@ -24,7 +24,7 @@ import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsRes
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapConfig; import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapConfig;
@Data @Data
public class EndpointSecurityInfo { public class TbLwM2MSecurityInfo {
private ValidateDeviceCredentialsResponse msg; private ValidateDeviceCredentialsResponse msg;
private SecurityInfo securityInfo; private SecurityInfo securityInfo;
private SecurityMode securityMode; private SecurityMode securityMode;

92
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/AbstractLwM2mTransportResource.java

@ -0,0 +1,92 @@
/**
* 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.extern.slf4j.Slf4j;
import org.eclipse.californium.core.coap.CoAP;
import org.eclipse.californium.core.coap.Response;
import org.eclipse.californium.core.server.resources.CoapExchange;
import org.eclipse.leshan.core.californium.LwM2mCoapResource;
import org.thingsboard.server.common.transport.TransportServiceCallback;
@Slf4j
public abstract class AbstractLwM2mTransportResource extends LwM2mCoapResource {
public AbstractLwM2mTransportResource(String name) {
super(name);
}
@Override
public void handleGET(CoapExchange exchange) {
processHandleGet(exchange);
}
@Override
public void handlePOST(CoapExchange exchange) {
processHandlePost(exchange);
}
protected abstract void processHandleGet(CoapExchange exchange);
protected abstract void processHandlePost(CoapExchange exchange);
public static class CoapOkCallback implements TransportServiceCallback<Void> {
private final CoapExchange exchange;
private final CoAP.ResponseCode onSuccessResponse;
private final CoAP.ResponseCode onFailureResponse;
public CoapOkCallback(CoapExchange exchange, CoAP.ResponseCode onSuccessResponse, CoAP.ResponseCode onFailureResponse) {
this.exchange = exchange;
this.onSuccessResponse = onSuccessResponse;
this.onFailureResponse = onFailureResponse;
}
@Override
public void onSuccess(Void msg) {
Response response = new Response(onSuccessResponse);
response.setAcknowledged(isConRequest());
exchange.respond(response);
}
@Override
public void onError(Throwable e) {
exchange.respond(onFailureResponse);
}
private boolean isConRequest() {
return exchange.advanced().getRequest().isConfirmable();
}
}
public static class CoapNoOpCallback implements TransportServiceCallback<Void> {
private final CoapExchange exchange;
CoapNoOpCallback(CoapExchange exchange) {
this.exchange = exchange;
}
@Override
public void onSuccess(Void msg) {
}
@Override
public void onError(Throwable e) {
exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR);
}
}
}

547
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java

@ -31,7 +31,6 @@ import org.eclipse.leshan.core.node.LwM2mObjectInstance;
import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mResource;
import org.eclipse.leshan.core.observation.Observation; import org.eclipse.leshan.core.observation.Observation;
import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.core.request.WriteRequest; import org.eclipse.leshan.core.request.WriteRequest;
import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.core.response.ReadResponse;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
@ -42,10 +41,10 @@ import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.cache.ota.OtaPackageDataCache;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.data.ota.OtaPackageKey; import org.thingsboard.server.common.data.ota.OtaPackageKey;
import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.ota.OtaPackageUtil; import org.thingsboard.server.common.data.ota.OtaPackageUtil;
import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.AdaptorException;
@ -58,10 +57,13 @@ import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper;
import org.thingsboard.server.transport.lwm2m.server.adaptors.LwM2MJsonAdaptor; import org.thingsboard.server.transport.lwm2m.server.adaptors.LwM2MJsonAdaptor;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientState;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientStateException;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; 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.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile;
import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientRpcRequest;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mFwSwUpdate;
import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue;
import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto; import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto;
import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters; import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters;
@ -87,21 +89,20 @@ import java.util.stream.Collectors;
import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST; import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST;
import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION; import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING;
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.INITIATED;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.CLIENT_NOT_AUTHORIZED;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEVICE_ATTRIBUTES_REQUEST; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEVICE_ATTRIBUTES_REQUEST;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_STATE_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_TELEMETRY; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_TELEMETRY;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LWM2M_STRATEGY_2; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_WARN;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL_ALL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL_ALL;
@ -109,8 +110,8 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_RESULT_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertJsonArrayToSet; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertJsonArrayToSet;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertOtaUpdateValueToString;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromIdVerToObjectId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromIdVerToObjectId;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getAckCallback; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getAckCallback;
@ -139,6 +140,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
public final LwM2mClientContext clientContext; public final LwM2mClientContext clientContext;
public final LwM2mTransportRequest lwM2mTransportRequest; public final LwM2mTransportRequest lwM2mTransportRequest;
private final Map<UUID, Long> rpcSubscriptions; private final Map<UUID, Long> rpcSubscriptions;
public final Map<String, Integer> firmwareUpdateState;
public DefaultLwM2MTransportMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper, public DefaultLwM2MTransportMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper,
LwM2mClientContext clientContext, LwM2mClientContext clientContext,
@ -154,6 +156,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
this.context = context; this.context = context;
this.adaptor = adaptor; this.adaptor = adaptor;
this.rpcSubscriptions = new ConcurrentHashMap<>(); this.rpcSubscriptions = new ConcurrentHashMap<>();
this.firmwareUpdateState = new ConcurrentHashMap<>();
this.sessionStore = sessionStore; this.sessionStore = sessionStore;
} }
@ -182,32 +185,39 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
*/ */
public void onRegistered(Registration registration, Collection<Observation> previousObservations) { public void onRegistered(Registration registration, Collection<Observation> previousObservations) {
registrationExecutor.submit(() -> { registrationExecutor.submit(() -> {
LwM2mClient lwM2MClient = this.clientContext.getClientByEndpoint(registration.getEndpoint());
try { try {
log.warn("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId()); log.warn("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId());
LwM2mClient lwM2MClient = this.clientContext.registerOrUpdate(registration);
if (lwM2MClient != null) { if (lwM2MClient != null) {
SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient); this.clientContext.register(lwM2MClient, registration);
if (sessionInfo != null) { this.sendLogsToThingsboard(lwM2MClient, LOG_LW2M_INFO + ": Client registered with registration id: " + registration.getId());
transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo, transportService)); SessionInfoProto sessionInfo = lwM2MClient.getSession();
TransportProtos.TransportToDeviceActorMsg msg = TransportProtos.TransportToDeviceActorMsg.newBuilder() transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo, transportService));
.setSessionInfo(sessionInfo) log.warn("40) sessionId [{}] Registering rpc subscription after Registration client", new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
.setSessionEvent(DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN)) TransportProtos.TransportToDeviceActorMsg msg = TransportProtos.TransportToDeviceActorMsg.newBuilder()
.setSubscribeToAttributes(TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build()) .setSessionInfo(sessionInfo)
.setSubscribeToRPC(TransportProtos.SubscribeToRPCMsg.newBuilder().build()) .setSessionEvent(DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN))
.build(); .setSubscribeToAttributes(TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().setSessionType(TransportProtos.SessionType.ASYNC).build())
transportService.process(msg, null); .setSubscribeToRPC(TransportProtos.SubscribeToRPCMsg.newBuilder().setSessionType(TransportProtos.SessionType.ASYNC).build())
this.getInfoFirmwareUpdate(lwM2MClient, null); .build();
this.getInfoSoftwareUpdate(lwM2MClient, null); transportService.process(msg, null);
this.initLwM2mFromClientValue(registration, lwM2MClient); this.getInfoFirmwareUpdate(lwM2MClient, null);
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration.getId()); this.getInfoSoftwareUpdate(lwM2MClient, null);
} else { this.initClientTelemetry(lwM2MClient);
log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); } else {
} log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), null);
}
} catch (LwM2MClientStateException stateException) {
if (LwM2MClientState.UNREGISTERED.equals(stateException.getState())) {
log.info("[{}] retry registration due to race condition: [{}].", registration.getEndpoint(), stateException.getState());
// Race condition detected and the client was in progress of unregistration while new registration arrived. Let's try again.
onRegistered(registration, previousObservations);
} else { } else {
log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), null); this.sendLogsToThingsboard(lwM2MClient, LOG_LW2M_WARN + ": Client registration failed due to invalid state: " + stateException.getState());
} }
} catch (Throwable t) { } catch (Throwable t) {
log.error("[{}] endpoint [{}] error Unable registration.", registration.getEndpoint(), t); log.error("[{}] endpoint [{}] error Unable registration.", registration.getEndpoint(), t);
this.sendLogsToThingsboard(lwM2MClient, LOG_LW2M_WARN + ": Client registration failed due to: " + t.getMessage());
} }
}); });
} }
@ -219,25 +229,26 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
*/ */
public void updatedReg(Registration registration) { public void updatedReg(Registration registration) {
updateRegistrationExecutor.submit(() -> { updateRegistrationExecutor.submit(() -> {
LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint());
try { try {
LwM2mClient client = clientContext.getOrRegister(registration); clientContext.updateRegistration(lwM2MClient, registration);
if (client != null && client.getSession() != null) { TransportProtos.SessionInfoProto sessionInfo = lwM2MClient.getSession();
SessionInfoProto sessionInfo = client.getSession(); this.reportActivityAndRegister(sessionInfo);
this.reportActivityAndRegister(sessionInfo); if (registration.usesQueueMode()) {
if (registration.getBindingMode().useQueueMode()) { LwM2mQueuedRequest request;
LwM2mQueuedRequest request; while ((request = lwM2MClient.getQueuedRequests().poll()) != null) {
while ((request = client.getQueuedRequests().poll()) != null) { request.send();
request.send();
}
} }
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client update Registration", registration.getId()); }
} catch (LwM2MClientStateException stateException) {
if (LwM2MClientState.REGISTERED.equals(stateException.getState())) {
log.info("[{}] update registration failed because client has different registration id: [{}] {}.", registration.getEndpoint(), stateException.getState(), stateException.getMessage());
} else { } else {
log.error("Client: [{}] updatedReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); onRegistered(registration, Collections.emptyList());
this.sendLogsToThingsboard(LOG_LW2M_ERROR + ": Client update Registration", registration.getId());
} }
} catch (Throwable t) { } catch (Throwable t) {
log.error("[{}] endpoint [{}] error Unable update registration.", registration.getEndpoint(), t); log.error("[{}] endpoint [{}] error Unable update registration.", registration.getEndpoint(), t);
this.sendLogsToThingsboard(LOG_LW2M_ERROR + String.format(": Client update Registration, %s", t.getMessage()), registration.getId()); this.sendLogsToThingsboard(lwM2MClient, LOG_LW2M_ERROR + String.format(": Client update Registration, %s", t.getMessage()));
} }
}); });
} }
@ -248,34 +259,32 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
*/ */
public void unReg(Registration registration, Collection<Observation> observations) { public void unReg(Registration registration, Collection<Observation> observations) {
unRegistrationExecutor.submit(() -> { unRegistrationExecutor.submit(() -> {
LwM2mClient client = clientContext.getClientByEndpoint(registration.getEndpoint());
try { try {
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId()); this.sendLogsToThingsboard(client, LOG_LW2M_INFO + ": Client unRegistration");
this.closeClientSession(registration); clientContext.unregister(client, registration);
SessionInfoProto sessionInfo = client.getSession();
if (sessionInfo != null) {
this.doCloseSession(sessionInfo);
transportService.deregisterSession(sessionInfo);
sessionStore.remove(registration.getEndpoint());
log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
} else {
log.error("Client close session: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
}
} catch (LwM2MClientStateException stateException) {
log.info("[{}] delete registration: [{}] {}.", registration.getEndpoint(), stateException.getState(), stateException.getMessage());
} catch (Throwable t) { } catch (Throwable t) {
log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t); log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t);
this.sendLogsToThingsboard(LOG_LW2M_ERROR + String.format(": Client Unable un Registration, %s", t.getMessage()), registration.getId()); this.sendLogsToThingsboard(client, LOG_LW2M_ERROR + String.format(": Client Unable un Registration, %s", t.getMessage()));
} }
}); });
} }
private void closeClientSession(Registration registration) {
SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(registration);
if (sessionInfo != null) {
transportService.deregisterSession(sessionInfo);
sessionStore.remove(registration.getEndpoint());
this.doCloseSession(sessionInfo);
clientContext.removeClientByRegistrationId(registration.getId());
log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
} else {
log.error("Client close session: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
}
}
@Override @Override
public void onSleepingDev(Registration registration) { public void onSleepingDev(Registration registration) {
log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint()); log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint());
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is sleeping!", registration.getId()); this.sendLogsToThingsboard(clientContext.getClientByEndpoint(registration.getEndpoint()), LOG_LW2M_INFO + ": Client is sleeping!");
//TODO: associate endpointId with device information. //TODO: associate endpointId with device information.
} }
@ -285,8 +294,11 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
@Override @Override
public void setCancelObservationsAll(Registration registration) { public void setCancelObservationsAll(Registration registration) {
if (registration != null) { if (registration != null) {
this.lwM2mTransportRequest.sendAllRequest(registration, null, OBSERVE_CANCEL_ALL, LwM2mClient client = clientContext.getClientByEndpoint(registration.getEndpoint());
null, null, this.config.getTimeout(), null); if (client != null && client.getRegistration() != null && client.getRegistration().getId().equals(registration.getId())) {
this.lwM2mTransportRequest.sendAllRequest(client, null, OBSERVE_CANCEL_ALL,
null, null, this.config.getTimeout(), null);
}
} }
} }
@ -298,9 +310,9 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @param response - observe * @param response - observe
*/ */
@Override @Override
public void onUpdateValueAfterReadResponse(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() != null) {
LwM2mClient lwM2MClient = clientContext.getOrRegister(registration); LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint());
ObjectModel objectModelVersion = lwM2MClient.getObjectModel(path, this.config.getModelProvider()); ObjectModel objectModelVersion = lwM2MClient.getObjectModel(path, this.config.getModelProvider());
if (objectModelVersion != null) { if (objectModelVersion != null) {
if (response.getContent() instanceof LwM2mObject) { if (response.getContent() instanceof LwM2mObject) {
@ -321,7 +333,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
} }
private void sendRpcRequestAfterReadResponse(Registration registration, LwM2mClient lwM2MClient, String pathIdVer, ReadResponse response, private void sendRpcRequestAfterReadResponse(Registration registration, LwM2mClient lwM2MClient, String pathIdVer, ReadResponse response,
Lwm2mClientRpcRequest rpcRequest) { LwM2mClientRpcRequest rpcRequest) {
Object value = null; Object value = null;
if (response.getContent() instanceof LwM2mObject) { if (response.getContent() instanceof LwM2mObject) {
value = lwM2MClient.objectToString((LwM2mObject) response.getContent(), this.converter, pathIdVer); value = lwM2MClient.objectToString((LwM2mObject) response.getContent(), this.converter, pathIdVer);
@ -332,7 +344,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
} }
String msg = String.format("%s: type operation %s path - %s value - %s", LOG_LW2M_INFO, String msg = String.format("%s: type operation %s path - %s value - %s", LOG_LW2M_INFO,
READ, pathIdVer, value); READ, pathIdVer, value);
this.sendLogsToThingsboard(msg, registration.getId()); this.sendLogsToThingsboard(lwM2MClient, msg);
rpcRequest.setValueMsg(String.format("%s", value)); rpcRequest.setValueMsg(String.format("%s", value));
this.sentRpcResponse(rpcRequest, response.getCode().getName(), (String) value, LOG_LW2M_VALUE); this.sentRpcResponse(rpcRequest, response.getCode().getName(), (String) value, LOG_LW2M_VALUE);
} }
@ -350,9 +362,9 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
*/ */
@Override @Override
public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) { public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {
LwM2mClient lwM2MClient = clientContext.getClient(sessionInfo); LwM2mClient lwM2MClient = clientContext.getClientBySessionInfo(sessionInfo);
if (msg.getSharedUpdatedCount() > 0 && lwM2MClient != null) { if (msg.getSharedUpdatedCount() > 0 && lwM2MClient != null) {
log.warn ("2) OnAttributeUpdate, SharedUpdatedList() [{}]", msg.getSharedUpdatedList()); log.warn("2) OnAttributeUpdate, SharedUpdatedList() [{}]", msg.getSharedUpdatedList());
msg.getSharedUpdatedList().forEach(tsKvProto -> { msg.getSharedUpdatedList().forEach(tsKvProto -> {
String pathName = tsKvProto.getKv().getKey(); String pathName = tsKvProto.getKv().getKey();
String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName); String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName);
@ -377,13 +389,13 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", pathIdVer, valueNew); 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", String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated",
LOG_LW2M_ERROR, pathIdVer, valueNew); LOG_LW2M_ERROR, pathIdVer, valueNew);
this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId()); this.sendLogsToThingsboard(lwM2MClient, logMsg);
} }
} else if (!isFwSwWords(pathName)) { } else if (!isFwSwWords(pathName)) {
log.error("Resource name name - [{}] value - [{}] is not present as attribute/telemetry in profile and cannot be updated", pathName, valueNew); 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", 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); LOG_LW2M_ERROR, pathName, valueNew);
this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId()); this.sendLogsToThingsboard(lwM2MClient, logMsg);
} }
}); });
@ -396,9 +408,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
} }
}); });
log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo); log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo);
} } else if (lwM2MClient == null) {
else if (lwM2MClient == null) { log.error("OnAttributeUpdate, lwM2MClient is null");
log.error ("OnAttributeUpdate, lwM2MClient is null");
} }
} }
@ -408,12 +419,11 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
*/ */
@Override @Override
public void onDeviceProfileUpdate(SessionInfoProto sessionInfo, DeviceProfile deviceProfile) { public void onDeviceProfileUpdate(SessionInfoProto sessionInfo, DeviceProfile deviceProfile) {
Set<LwM2mClient> clients = clientContext.getLwM2mClients() List<LwM2mClient> clients = clientContext.getLwM2mClients()
.stream().filter(e -> e.getProfileId().equals(deviceProfile.getUuidId())).collect(Collectors.toSet()); .stream().filter(e -> e.getProfileId().equals(deviceProfile.getUuidId())).collect(Collectors.toList());
clients.forEach(client -> client.onDeviceProfileUpdate(deviceProfile)); clients.forEach(client -> client.onDeviceProfileUpdate(deviceProfile));
Set<String> registrationIds = clients.stream().map(LwM2mClient::getRegistration).map(Registration::getId).collect(Collectors.toSet()); if (clients.size() > 0) {
if (registrationIds.size() > 0) { this.onDeviceProfileUpdate(clients, deviceProfile);
this.onDeviceProfileUpdate(registrationIds, deviceProfile);
} }
} }
@ -440,50 +450,65 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
/** /**
* #1 del from rpcSubscriptions by timeout * #1 del from rpcSubscriptions by timeout
* #2 if not present in rpcSubscriptions by requestId: create new Lwm2mClientRpcRequest, after success - add requestId, timeout * #2 if not present in rpcSubscriptions by requestId: create new LwM2mClientRpcRequest, after success - add requestId, timeout
*/ */
@Override @Override
public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg, SessionInfoProto sessionInfo) { public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg, SessionInfoProto sessionInfo) {
// #1 // #1
this.checkRpcRequestTimeout(); this.checkRpcRequestTimeout();
log.warn ("4) toDeviceRpcRequestMsg: [{}], sessionUUID: [{}]", toDeviceRpcRequestMsg, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); log.warn("4) toDeviceRpcRequestMsg: [{}], sessionUUID: [{}]", toDeviceRpcRequestMsg, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
String bodyParams = StringUtils.trimToNull(toDeviceRpcRequestMsg.getParams()) != null ? toDeviceRpcRequestMsg.getParams() : "null"; String bodyParams = StringUtils.trimToNull(toDeviceRpcRequestMsg.getParams()) != null ? toDeviceRpcRequestMsg.getParams() : "null";
LwM2mTypeOper lwM2mTypeOper = setValidTypeOper(toDeviceRpcRequestMsg.getMethodName()); LwM2mTypeOper lwM2mTypeOper = setValidTypeOper(toDeviceRpcRequestMsg.getMethodName());
UUID requestUUID = new UUID(toDeviceRpcRequestMsg.getRequestIdMSB(), toDeviceRpcRequestMsg.getRequestIdLSB()); UUID requestUUID = new UUID(toDeviceRpcRequestMsg.getRequestIdMSB(), toDeviceRpcRequestMsg.getRequestIdLSB());
if (!this.rpcSubscriptions.containsKey(requestUUID)) { if (!this.rpcSubscriptions.containsKey(requestUUID)) {
this.rpcSubscriptions.put(requestUUID, toDeviceRpcRequestMsg.getExpirationTime()); this.rpcSubscriptions.put(requestUUID, toDeviceRpcRequestMsg.getExpirationTime());
Lwm2mClientRpcRequest lwm2mClientRpcRequest = null; LwM2mClientRpcRequest lwm2mClientRpcRequest = null;
try { try {
Registration registration = clientContext.getClient(sessionInfo).getRegistration(); LwM2mClient client = clientContext.getClientBySessionInfo(sessionInfo);
lwm2mClientRpcRequest = new Lwm2mClientRpcRequest(lwM2mTypeOper, bodyParams, toDeviceRpcRequestMsg.getRequestId(), sessionInfo, registration, this); Registration registration = client.getRegistration();
if (lwm2mClientRpcRequest.getErrorMsg() != null) { if(registration != null) {
lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name()); lwm2mClientRpcRequest = new LwM2mClientRpcRequest(lwM2mTypeOper, bodyParams, toDeviceRpcRequestMsg.getRequestId(), sessionInfo, registration, this);
this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo); if (lwm2mClientRpcRequest.getErrorMsg() != null) {
lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name());
this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo);
} else {
lwM2mTransportRequest.sendAllRequest(client, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(),
null,
lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(),
this.config.getTimeout(), lwm2mClientRpcRequest);
}
} else { } else {
lwM2mTransportRequest.sendAllRequest(registration, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(), this.sendErrorRpcResponse(lwm2mClientRpcRequest, "registration == null", sessionInfo);
null,
lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(),
this.config.getTimeout(), lwm2mClientRpcRequest);
} }
} catch (Exception e) { } catch (Exception e) {
if (lwm2mClientRpcRequest == null) { this.sendErrorRpcResponse(lwm2mClientRpcRequest, e.getMessage(), sessionInfo);
lwm2mClientRpcRequest = new Lwm2mClientRpcRequest();
}
lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name());
if (lwm2mClientRpcRequest.getErrorMsg() == null) {
lwm2mClientRpcRequest.setErrorMsg(e.getMessage());
}
this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo);
} }
} }
} }
private void sendErrorRpcResponse(LwM2mClientRpcRequest lwm2mClientRpcRequest, String msgError, SessionInfoProto sessionInfo) {
if (lwm2mClientRpcRequest == null) {
lwm2mClientRpcRequest = new LwM2mClientRpcRequest();
}
lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name());
if (lwm2mClientRpcRequest.getErrorMsg() == null) {
lwm2mClientRpcRequest.setErrorMsg(msgError);
}
this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo);
}
private void checkRpcRequestTimeout() { private void checkRpcRequestTimeout() {
Set<UUID> rpcSubscriptionsToRemove = rpcSubscriptions.entrySet().stream().filter(kv -> System.currentTimeMillis() > kv.getValue()).map(Map.Entry::getKey).collect(Collectors.toSet()); log.warn("4.1) before rpcSubscriptions.size(): [{}]", rpcSubscriptions.size());
rpcSubscriptionsToRemove.forEach(rpcSubscriptions::remove); if (rpcSubscriptions.size() > 0) {
Set<UUID> rpcSubscriptionsToRemove = rpcSubscriptions.entrySet().stream().filter(kv -> System.currentTimeMillis() > kv.getValue()).map(Map.Entry::getKey).collect(Collectors.toSet());
log.warn("4.2) System.currentTimeMillis(): [{}]", System.currentTimeMillis());
log.warn("4.3) rpcSubscriptionsToRemove: [{}]", rpcSubscriptionsToRemove);
rpcSubscriptionsToRemove.forEach(rpcSubscriptions::remove);
}
log.warn("4.4) after rpcSubscriptions.size(): [{}]", rpcSubscriptions.size());
} }
public void sentRpcResponse(Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) { public void sentRpcResponse(LwM2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) {
rpcRequest.setResponseCode(requestCode); rpcRequest.setResponseCode(requestCode);
if (LOG_LW2M_ERROR.equals(typeMsg)) { if (LOG_LW2M_ERROR.equals(typeMsg)) {
rpcRequest.setInfoMsg(null); rpcRequest.setInfoMsg(null);
@ -506,7 +531,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
@Override @Override
public void onToDeviceRpcResponse(TransportProtos.ToDeviceRpcResponseMsg toDeviceResponse, SessionInfoProto sessionInfo) { public void onToDeviceRpcResponse(TransportProtos.ToDeviceRpcResponseMsg toDeviceResponse, SessionInfoProto sessionInfo) {
log.warn ("5) onToDeviceRpcResponse: [{}], sessionUUID: [{}]", toDeviceResponse, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); log.warn("5) onToDeviceRpcResponse: [{}], sessionUUID: [{}]", toDeviceResponse, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
transportService.process(sessionInfo, toDeviceResponse, null); transportService.process(sessionInfo, toDeviceResponse, null);
} }
@ -514,17 +539,6 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
log.info("[{}] toServerRpcResponse", toServerResponse); log.info("[{}] toServerRpcResponse", toServerResponse);
} }
/**
* Trigger Server path = "/1/0/8"
* <p>
* Trigger bootStrap path = "/1/0/9" - have to implemented on client
*/
@Override
public void doTrigger(Registration registration, String path) {
lwM2mTransportRequest.sendAllRequest(registration, path, EXECUTE,
ContentFormat.TLV.getName(), null, this.config.getTimeout(), null);
}
/** /**
* Deregister session in transport * Deregister session in transport
* *
@ -558,7 +572,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
@Override @Override
public void onAwakeDev(Registration registration) { public void onAwakeDev(Registration registration) {
log.trace("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint()); log.trace("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint());
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is awake!", registration.getId()); this.sendLogsToThingsboard(clientContext.getClientByEndpoint(registration.getEndpoint()), LOG_LW2M_INFO + ": Client is awake!");
//TODO: associate endpointId with device information. //TODO: associate endpointId with device information.
} }
@ -567,13 +581,17 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @param registrationId - Id of Registration LwM2M Client * @param registrationId - Id of Registration LwM2M Client
*/ */
@Override @Override
public void sendLogsToThingsboard(String logMsg, String registrationId) { public void sendLogsToThingsboard(String registrationId, String logMsg) {
SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(registrationId); sendLogsToThingsboard(clientContext.getClientByRegistrationId(registrationId), logMsg);
if (logMsg != null && sessionInfo != null) { }
@Override
public void sendLogsToThingsboard(LwM2mClient client, String logMsg) {
if (logMsg != null && client != null && client.getSession() != null) {
if (logMsg.length() > 1024) { if (logMsg.length() > 1024) {
logMsg = logMsg.substring(0, 1024); logMsg = logMsg.substring(0, 1024);
} }
this.helper.sendParametersOnThingsboardTelemetry(this.helper.getKvStringtoThingsboard(LOG_LW2M_TELEMETRY, logMsg), sessionInfo); this.helper.sendParametersOnThingsboardTelemetry(this.helper.getKvStringtoThingsboard(LOG_LW2M_TELEMETRY, logMsg), client.getSession());
} }
} }
@ -586,24 +604,23 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* - Read Request to the client after registration to read all resource values for all objects * - Read Request to the client after registration to read all resource values for all objects
* - then Observe Request to the client marked as observe from the profile configuration. * - then Observe Request to the client marked as observe from the profile configuration.
* *
* @param registration - Registration LwM2M Client * @param lwM2MClient - object with All parameters off client
* @param lwM2MClient - object with All parameters off client
*/ */
private void initLwM2mFromClientValue(Registration registration, LwM2mClient lwM2MClient) { private void initClientTelemetry(LwM2mClient lwM2MClient) {
LwM2mClientProfile lwM2MClientProfile = clientContext.getProfile(registration); LwM2mClientProfile lwM2MClientProfile = clientContext.getProfile(lwM2MClient.getProfileId());
Set<String> clientObjects = clientContext.getSupportedIdVerInClient(registration); Set<String> clientObjects = clientContext.getSupportedIdVerInClient(lwM2MClient);
if (clientObjects != null && clientObjects.size() > 0) { if (clientObjects != null && clientObjects.size() > 0) {
if (LWM2M_STRATEGY_2 == LwM2mTransportUtil.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) { if (LwM2mTransportUtil.LwM2MClientStrategy.CLIENT_STRATEGY_2.code == lwM2MClientProfile.getClientStrategy()) {
// #2 // #2
lwM2MClient.getPendingReadRequests().addAll(clientObjects); lwM2MClient.getPendingReadRequests().addAll(clientObjects);
clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, READ, ContentFormat.TLV.getName(), clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(lwM2MClient, path, READ,
null, this.config.getTimeout(), null)); null, this.config.getTimeout(), null));
} }
// #1 // #1
this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, READ, clientObjects); this.initReadAttrTelemetryObserveToClient(lwM2MClient, READ, clientObjects);
this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, OBSERVE, clientObjects); this.initReadAttrTelemetryObserveToClient(lwM2MClient, OBSERVE, clientObjects);
this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, WRITE_ATTRIBUTES, clientObjects); this.initReadAttrTelemetryObserveToClient(lwM2MClient, WRITE_ATTRIBUTES, clientObjects);
this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, DISCOVER, clientObjects); this.initReadAttrTelemetryObserveToClient(lwM2MClient, DISCOVER, clientObjects);
} }
} }
@ -645,65 +662,23 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @param path - resource * @param path - resource
*/ */
private void updateResourcesValue(Registration registration, LwM2mResource lwM2mResource, String path) { private void updateResourcesValue(Registration registration, LwM2mResource lwM2mResource, String path) {
LwM2mClient lwM2MClient = clientContext.getOrRegister(registration); LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint());
if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.config.getModelProvider())) { if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.config.getModelProvider())) {
/** version != null /** version != null
* set setClient_fw_info... = value * set setClient_fw_info... = value
**/ **/
if (lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) { if (lwM2MClient.getFwUpdate() != null && lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) {
lwM2MClient.getFwUpdate().initReadValue(this, this.lwM2mTransportRequest, path); lwM2MClient.getFwUpdate().initReadValue(this, this.lwM2mTransportRequest, path);
} }
if (lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) { if (lwM2MClient.getSwUpdate() != null && lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) {
lwM2MClient.getSwUpdate().initReadValue(this, this.lwM2mTransportRequest, path); lwM2MClient.getSwUpdate().initReadValue(this, this.lwM2mTransportRequest, path);
} }
/** if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path)) ||
* Before operation Execute (FwUpdate) inspection Update Result : (convertPathFromObjectIdToIdVer(FW_STATE_ID, registration).equals(path))) {
* - after finished operation Write result: success (FwUpdate): fw_state = DOWNLOADED LwM2mFwSwUpdate fwUpdate = lwM2MClient.getFwUpdate(clientContext);
* - before start operation Execute (FwUpdate) Update Result = 0 - Initial value log.warn("93) path: [{}] value: [{}]", path, lwM2mResource.getValue());
* - start Execute (FwUpdate) fwUpdate.updateStateOta(this, lwM2mTransportRequest, registration, path, ((Long) lwM2mResource.getValue()).intValue());
* After finished operation Execute (FwUpdate) inspection Update Result :
* - after start operation Execute (FwUpdate): fw_state = UPDATING
* - after success finished operation Execute (FwUpdate) Update Result == 1 ("Firmware updated successfully")
* - finished operation Execute (FwUpdate)
*/
if (lwM2MClient.getFwUpdate() != null
&& (convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) {
if (DOWNLOADED.name().equals(lwM2MClient.getFwUpdate().getStateUpdate())
&& lwM2MClient.getFwUpdate().conditionalFwExecuteStart()) {
lwM2MClient.getFwUpdate().executeFwSwWare(this, this.lwM2mTransportRequest);
} else if (UPDATING.name().equals(lwM2MClient.getFwUpdate().getStateUpdate())
&& lwM2MClient.getFwUpdate().conditionalFwExecuteAfterSuccess()) {
lwM2MClient.getFwUpdate().finishFwSwUpdate(this, true);
} else if (UPDATING.name().equals(lwM2MClient.getFwUpdate().getStateUpdate())
&& lwM2MClient.getFwUpdate().conditionalFwExecuteAfterError()) {
lwM2MClient.getFwUpdate().finishFwSwUpdate(this, false);
}
}
/**
* Before operation Execute (SwUpdate) inspection Update Result :
* - after finished operation Write result: success (SwUpdate): fw_state = DOWNLOADED
* - before operation Execute (SwUpdate) Update Result = 3 - Successfully Downloaded and package integrity verified
* - start Execute (SwUpdate)
* After finished operation Execute (SwUpdate) inspection Update Result :
* - after start operation Execute (SwUpdate): fw_state = UPDATING
* - after success finished operation Execute (SwUpdate) Update Result == 2 "Software successfully installed.""
* - after success finished operation Execute (SwUpdate) Update Result == 2 "Software successfully installed.""
* - finished operation Execute (SwUpdate)
*/
if (lwM2MClient.getSwUpdate() != null
&& (convertPathFromObjectIdToIdVer(SW_RESULT_ID, registration).equals(path))) {
if (DOWNLOADED.name().equals(lwM2MClient.getSwUpdate().getStateUpdate())
&& lwM2MClient.getSwUpdate().conditionalSwUpdateExecute()) {
lwM2MClient.getSwUpdate().executeFwSwWare(this, this.lwM2mTransportRequest);
} else if (UPDATING.name().equals(lwM2MClient.getSwUpdate().getStateUpdate())
&& lwM2MClient.getSwUpdate().conditionalSwExecuteAfterSuccess()) {
lwM2MClient.getSwUpdate().finishFwSwUpdate(this, true);
} else if (UPDATING.name().equals(lwM2MClient.getSwUpdate().getStateUpdate())
&& lwM2MClient.getSwUpdate().conditionalSwExecuteAfterError()) {
lwM2MClient.getSwUpdate().finishFwSwUpdate(this, false);
}
} }
Set<String> paths = new HashSet<>(); Set<String> paths = new HashSet<>();
paths.add(path); paths.add(path);
@ -740,15 +715,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
} }
} }
/** private void initReadAttrTelemetryObserveToClient(LwM2mClient lwM2MClient, LwM2mTypeOper typeOper, Set<String> clientObjects) {
* Start observe/read: Attr/Telemetry LwM2mClientProfile lwM2MClientProfile = clientContext.getProfile(lwM2MClient.getProfileId());
* #1 - Analyze: path in resource profile == client resource
*
* @param registration -
*/
private void initReadAttrTelemetryObserveToClient(Registration registration, LwM2mClient lwM2MClient,
LwM2mTypeOper typeOper, Set<String> clientObjects) {
LwM2mClientProfile lwM2MClientProfile = clientContext.getProfile(registration);
Set<String> result = null; Set<String> result = null;
ConcurrentHashMap<String, Object> params = null; ConcurrentHashMap<String, Object> params = null;
if (READ.equals(typeOper)) { if (READ.equals(typeOper)) {
@ -768,28 +736,42 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile()); params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile());
result = params.keySet(); result = params.keySet();
} }
if (result != null && !result.isEmpty()) { sendRequestsToClient(lwM2MClient, typeOper, clientObjects, result, params);
// #1 }
Set<String> pathSend = result.stream().filter(target -> {
return target.split(LWM2M_SEPARATOR_PATH).length < 3 ? private void sendRequestsToClient(LwM2mClient lwM2MClient, LwM2mTypeOper operationType, Set<String> supportedObjectIds, Set<String> desiredObjectIds, ConcurrentHashMap<String, Object> params) {
clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]) : if (desiredObjectIds != null && !desiredObjectIds.isEmpty()) {
clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1] + "/" + target.split(LWM2M_SEPARATOR_PATH)[2]); Set<String> targetObjectIds = desiredObjectIds.stream().filter(target -> isSupportedTargetId(supportedObjectIds, target)
}
).collect(Collectors.toUnmodifiableSet()); ).collect(Collectors.toUnmodifiableSet());
if (!pathSend.isEmpty()) { if (!targetObjectIds.isEmpty()) {
lwM2MClient.getPendingReadRequests().addAll(pathSend); //TODO: remove this side effect?
ConcurrentHashMap<String, Object> finalParams = params; lwM2MClient.getPendingReadRequests().addAll(targetObjectIds);
pathSend.forEach(target -> { targetObjectIds.forEach(target -> {
lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(), Object additionalParams = params != null ? params.get(target) : null;
finalParams != null ? finalParams.get(target) : null, this.config.getTimeout(), null); lwM2mTransportRequest.sendAllRequest(lwM2MClient, target, operationType, additionalParams, this.config.getTimeout(), null);
}); });
if (OBSERVE.equals(typeOper)) { if (OBSERVE.equals(operationType)) {
lwM2MClient.initReadValue(this, null); lwM2MClient.initReadValue(this, null);
} }
} }
} }
} }
private boolean isSupportedTargetId(Set<String> supportedIds, String targetId) {
String[] targetIdParts = targetId.split(LWM2M_SEPARATOR_PATH);
if (targetIdParts.length <= 1) {
return false;
}
String targetIdSearch = targetIdParts[0];
for (int i = 1; i < targetIdParts.length; i++) {
targetIdSearch += "/" + targetIdParts[i];
if (supportedIds.contains(targetIdSearch)) {
return true;
}
}
return false;
}
private ConcurrentHashMap<String, Object> getPathForWriteAttributes(JsonObject objectJson) { private ConcurrentHashMap<String, Object> getPathForWriteAttributes(JsonObject objectJson) {
ConcurrentHashMap<String, Object> pathAttributes = new Gson().fromJson(objectJson.toString(), ConcurrentHashMap<String, Object> pathAttributes = new Gson().fromJson(objectJson.toString(),
new TypeToken<ConcurrentHashMap<String, Object>>() { new TypeToken<ConcurrentHashMap<String, Object>>() {
@ -798,7 +780,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
} }
private void onDeviceUpdate(LwM2mClient lwM2MClient, Device device, Optional<DeviceProfile> deviceProfileOpt) { private void onDeviceUpdate(LwM2mClient lwM2MClient, Device device, Optional<DeviceProfile> deviceProfileOpt) {
deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceProfileUpdate(Collections.singleton(lwM2MClient.getRegistration().getId()), deviceProfile)); deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceProfileUpdate(Collections.singletonList(lwM2MClient), deviceProfile));
lwM2MClient.onDeviceUpdate(device, deviceProfileOpt); lwM2MClient.onDeviceUpdate(device, deviceProfileOpt);
} }
@ -843,7 +825,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
} }
private TransportProtos.KeyValueProto getKvToThingsboard(String pathIdVer, Registration registration) { private TransportProtos.KeyValueProto getKvToThingsboard(String pathIdVer, Registration registration) {
LwM2mClient lwM2MClient = this.clientContext.getClientByRegistrationId(registration.getId()); LwM2mClient lwM2MClient = this.clientContext.getClientByEndpoint(registration.getEndpoint());
JsonObject names = clientContext.getProfiles().get(lwM2MClient.getProfileId()).getPostKeyNameProfile(); JsonObject names = clientContext.getProfiles().get(lwM2MClient.getProfileId()).getPostKeyNameProfile();
if (names != null && names.has(pathIdVer)) { if (names != null && names.has(pathIdVer)) {
String resourceName = names.get(pathIdVer).getAsString(); String resourceName = names.get(pathIdVer).getAsString();
@ -858,8 +840,9 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
valueKvProto = new JsonObject(); valueKvProto = new JsonObject();
Object finalvalueKvProto = valueKvProto; Object finalvalueKvProto = valueKvProto;
Gson gson = new GsonBuilder().create(); Gson gson = new GsonBuilder().create();
resourceValue.getValues().forEach((k, v) -> { ResourceModel.Type finalCurrentType = currentType;
Object val = this.converter.convertValue(v, currentType, expectedType, resourceValue.getInstances().forEach((k, v) -> {
Object val = this.converter.convertValue(v, finalCurrentType, expectedType,
new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))); new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)));
JsonElement element = gson.toJsonTree(val, val.getClass()); JsonElement element = gson.toJsonTree(val, val.getClass());
((JsonObject) finalvalueKvProto).add(String.valueOf(k), element); ((JsonObject) finalvalueKvProto).add(String.valueOf(k), element);
@ -869,6 +852,9 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
valueKvProto = this.converter.convertValue(resourceValue.getValue(), currentType, expectedType, valueKvProto = this.converter.convertValue(resourceValue.getValue(), currentType, expectedType,
new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))); new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)));
} }
LwM2mOtaConvert lwM2mOtaConvert = convertOtaUpdateValueToString(pathIdVer, valueKvProto, currentType);
valueKvProto = lwM2mOtaConvert.getValue();
currentType = lwM2mOtaConvert.getCurrentType();
return valueKvProto != null ? this.helper.getKvAttrTelemetryToThingsboard(currentType, resourceName, valueKvProto, resourceValue.isMultiInstances()) : null; return valueKvProto != null ? this.helper.getKvAttrTelemetryToThingsboard(currentType, resourceName, valueKvProto, resourceValue.isMultiInstances()) : null;
} }
} catch (Exception e) { } catch (Exception e) {
@ -892,9 +878,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer); ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer);
return this.converter.convertValue(resourceValue.getValue(), currentType, expectedType, return this.converter.convertValue(resourceValue.getValue(), currentType, expectedType,
new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))); new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)));
} } else {
else {
return null; return null;
} }
} }
@ -955,10 +939,10 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* #6.1 - update WriteAttribute * #6.1 - update WriteAttribute
* #6.2 - del WriteAttribute * #6.2 - del WriteAttribute
* *
* @param registrationIds - * @param clients -
* @param deviceProfile - * @param deviceProfile -
*/ */
private void onDeviceProfileUpdate(Set<String> registrationIds, DeviceProfile deviceProfile) { private void onDeviceProfileUpdate(List<LwM2mClient> clients, DeviceProfile deviceProfile) {
LwM2mClientProfile lwM2MClientProfileOld = clientContext.getProfiles().get(deviceProfile.getUuidId()).clone(); LwM2mClientProfile lwM2MClientProfileOld = clientContext.getProfiles().get(deviceProfile.getUuidId()).clone();
if (clientContext.profileUpdate(deviceProfile) != null) { if (clientContext.profileUpdate(deviceProfile) != null) {
// #1 // #1
@ -1009,15 +993,14 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
// #3.4, #6 // #3.4, #6
if (!attributeLwm2mOld.equals(attributeLwm2mNew)) { if (!attributeLwm2mOld.equals(attributeLwm2mNew)) {
this.getAnalyzerAttributeLwm2m(registrationIds, attributeLwm2mOld, attributeLwm2mNew); this.getAnalyzerAttributeLwm2m(clients, attributeLwm2mOld, attributeLwm2mNew);
} }
// #4.1 add // #4.1 add
if (sendAttrToThingsboard.getPathPostParametersAdd().size() > 0) { if (sendAttrToThingsboard.getPathPostParametersAdd().size() > 0) {
// update value in Resources // update value in Resources
registrationIds.forEach(registrationId -> { clients.forEach(client -> {
Registration registration = clientContext.getRegistration(registrationId); this.readObserveFromProfile(client, sendAttrToThingsboard.getPathPostParametersAdd(), READ);
this.readObserveFromProfile(registration, sendAttrToThingsboard.getPathPostParametersAdd(), READ);
}); });
} }
// #4.2 del // #4.2 del
@ -1041,15 +1024,15 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
// does not include oldObserve // does not include oldObserve
ResultsAnalyzerParameters postObserveAnalyzer = this.getAnalyzerParameters(sendObserveToClientOld.getPathPostParametersAdd(), sendObserveToClientNew.getPathPostParametersAdd()); ResultsAnalyzerParameters postObserveAnalyzer = this.getAnalyzerParameters(sendObserveToClientOld.getPathPostParametersAdd(), sendObserveToClientNew.getPathPostParametersAdd());
// send Request observe to Client // send Request observe to Client
registrationIds.forEach(registrationId -> { clients.forEach(client -> {
Registration registration = clientContext.getRegistration(registrationId); Registration registration = client.getRegistration();
if (postObserveAnalyzer.getPathPostParametersAdd().size() > 0) { if (postObserveAnalyzer.getPathPostParametersAdd().size() > 0) {
this.readObserveFromProfile(registration, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE); this.readObserveFromProfile(client, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE);
} }
// 5.3 del // 5.3 del
// send Request cancel observe to Client // send Request cancel observe to Client
if (postObserveAnalyzer.getPathPostParametersDel().size() > 0) { if (postObserveAnalyzer.getPathPostParametersDel().size() > 0) {
this.cancelObserveFromProfile(registration, postObserveAnalyzer.getPathPostParametersDel()); this.cancelObserveFromProfile(client, postObserveAnalyzer.getPathPostParametersDel());
} }
}); });
} }
@ -1086,19 +1069,18 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* Update Resource value after change RezAttrTelemetry in config Profile * Update Resource value after change RezAttrTelemetry in config Profile
* send response Read to Client and add path to pathResAttrTelemetry in LwM2MClient.getAttrTelemetryObserveValue() * send response Read to Client and add path to pathResAttrTelemetry in LwM2MClient.getAttrTelemetryObserveValue()
* *
* @param registration - Registration LwM2M Client * @param targets - path Resources == [ "/2/0/0", "/2/0/1"]
* @param targets - path Resources == [ "/2/0/0", "/2/0/1"]
*/ */
private void readObserveFromProfile(Registration registration, Set<String> targets, LwM2mTypeOper typeOper) { private void readObserveFromProfile(LwM2mClient client, Set<String> targets, LwM2mTypeOper typeOper) {
targets.forEach(target -> { targets.forEach(target -> {
LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(target)); LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(target));
if (pathIds.isResource()) { if (pathIds.isResource()) {
if (READ.equals(typeOper)) { if (READ.equals(typeOper)) {
lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, lwM2mTransportRequest.sendAllRequest(client, target, typeOper,
ContentFormat.TLV.getName(), null, this.config.getTimeout(), null); null, this.config.getTimeout(), null);
} else if (OBSERVE.equals(typeOper)) { } else if (OBSERVE.equals(typeOper)) {
lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, lwM2mTransportRequest.sendAllRequest(client, target, typeOper,
null, null, this.config.getTimeout(), null); null, this.config.getTimeout(), null);
} }
} }
}); });
@ -1124,7 +1106,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @param attributeLwm2mNew - * @param attributeLwm2mNew -
* @return * @return
*/ */
private void getAnalyzerAttributeLwm2m(Set<String> registrationIds, JsonObject attributeLwm2mOld, JsonObject attributeLwm2mNew) { private void getAnalyzerAttributeLwm2m(List<LwM2mClient> clients, JsonObject attributeLwm2mOld, JsonObject attributeLwm2mNew) {
ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters(); ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
ConcurrentHashMap<String, Object> lwm2mAttributesOld = new Gson().fromJson(attributeLwm2mOld.toString(), ConcurrentHashMap<String, Object> lwm2mAttributesOld = new Gson().fromJson(attributeLwm2mOld.toString(),
new TypeToken<ConcurrentHashMap<String, Object>>() { new TypeToken<ConcurrentHashMap<String, Object>>() {
@ -1146,23 +1128,22 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
// #6 // #6
// #6.2 // #6.2
if (analyzerParameters.getPathPostParametersAdd().size() > 0) { if (analyzerParameters.getPathPostParametersAdd().size() > 0) {
registrationIds.forEach(registrationId -> { clients.forEach(client -> {
Registration registration = this.clientContext.getRegistration(registrationId); Set<String> clientObjects = clientContext.getSupportedIdVerInClient(client);
Set<String> clientObjects = clientContext.getSupportedIdVerInClient(registration);
Set<String> pathSend = analyzerParameters.getPathPostParametersAdd().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1])) Set<String> pathSend = analyzerParameters.getPathPostParametersAdd().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]))
.collect(Collectors.toUnmodifiableSet()); .collect(Collectors.toUnmodifiableSet());
if (!pathSend.isEmpty()) { if (!pathSend.isEmpty()) {
ConcurrentHashMap<String, Object> finalParams = lwm2mAttributesNew; ConcurrentHashMap<String, Object> finalParams = lwm2mAttributesNew;
pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(registration, target, WRITE_ATTRIBUTES, ContentFormat.TLV.getName(), pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(client, target, WRITE_ATTRIBUTES,
finalParams.get(target), this.config.getTimeout(), null)); finalParams.get(target), this.config.getTimeout(), null));
} }
}); });
} }
// #6.2 // #6.2
if (analyzerParameters.getPathPostParametersDel().size() > 0) { if (analyzerParameters.getPathPostParametersDel().size() > 0) {
registrationIds.forEach(registrationId -> { clients.forEach(client -> {
Registration registration = this.clientContext.getRegistration(registrationId); Registration registration = client.getRegistration();
Set<String> clientObjects = clientContext.getSupportedIdVerInClient(registration); Set<String> clientObjects = clientContext.getSupportedIdVerInClient(client);
Set<String> pathSend = analyzerParameters.getPathPostParametersDel().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1])) Set<String> pathSend = analyzerParameters.getPathPostParametersDel().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]))
.collect(Collectors.toUnmodifiableSet()); .collect(Collectors.toUnmodifiableSet());
if (!pathSend.isEmpty()) { if (!pathSend.isEmpty()) {
@ -1170,8 +1151,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
Map<String, Object> params = (Map<String, Object>) lwm2mAttributesOld.get(target); Map<String, Object> params = (Map<String, Object>) lwm2mAttributesOld.get(target);
params.clear(); params.clear();
params.put(OBJECT_VERSION, ""); params.put(OBJECT_VERSION, "");
lwM2mTransportRequest.sendAllRequest(registration, target, WRITE_ATTRIBUTES, ContentFormat.TLV.getName(), lwM2mTransportRequest.sendAllRequest(client, target, WRITE_ATTRIBUTES, params, this.config.getTimeout(), null);
params, this.config.getTimeout(), null);
}); });
} }
}); });
@ -1179,12 +1159,10 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
} }
private void cancelObserveFromProfile(Registration registration, Set<String> paramAnallyzer) { private void cancelObserveFromProfile(LwM2mClient lwM2mClient, Set<String> paramAnallyzer) {
LwM2mClient lwM2MClient = clientContext.getOrRegister(registration);
paramAnallyzer.forEach(pathIdVer -> { paramAnallyzer.forEach(pathIdVer -> {
if (this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) != null) { if (this.getResourceValueFromLwM2MClient(lwM2mClient, pathIdVer) != null) {
lwM2mTransportRequest.sendAllRequest(registration, pathIdVer, OBSERVE_CANCEL, null, lwM2mTransportRequest.sendAllRequest(lwM2mClient, pathIdVer, OBSERVE_CANCEL, null, this.config.getTimeout(), null);
null, this.config.getTimeout(), null);
} }
} }
); );
@ -1192,14 +1170,12 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
private void updateResourcesValueToClient(LwM2mClient lwM2MClient, Object valueOld, Object valueNew, String path) { private void updateResourcesValueToClient(LwM2mClient lwM2MClient, Object valueOld, Object valueNew, String path) {
if (valueNew != null && (valueOld == null || !valueNew.toString().equals(valueOld.toString()))) { if (valueNew != null && (valueOld == null || !valueNew.toString().equals(valueOld.toString()))) {
lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), path, WRITE_REPLACE, lwM2mTransportRequest.sendAllRequest(lwM2MClient, path, WRITE_REPLACE, valueNew, this.config.getTimeout(), null);
ContentFormat.TLV.getName(), valueNew,
this.config.getTimeout(), null);
} else { } else {
log.error("Failed update resource [{}] [{}]", path, valueNew); 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", String logMsg = String.format("%s: Failed update resource path - %s value - %s. Value is not changed or bad",
LOG_LW2M_ERROR, path, valueNew); LOG_LW2M_ERROR, path, valueNew);
this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId()); this.sendLogsToThingsboard(lwM2MClient, logMsg);
log.info("Failed update resource [{}] [{}]", path, valueNew); log.info("Failed update resource [{}] [{}]", path, valueNew);
} }
} }
@ -1221,7 +1197,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
*/ */
public String getPresentPathIntoProfile(TransportProtos.SessionInfoProto sessionInfo, String name) { public String getPresentPathIntoProfile(TransportProtos.SessionInfoProto sessionInfo, String name) {
LwM2mClientProfile profile = clientContext.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); LwM2mClientProfile profile = clientContext.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
LwM2mClient lwM2mClient = clientContext.getClient(sessionInfo); LwM2mClient lwM2mClient = clientContext.getClientBySessionInfo(sessionInfo);
return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream() return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream()
.filter(e -> e.getValue().getAsString().equals(name) && validateResourceInModel(lwM2mClient, e.getKey(), false)).findFirst().map(Map.Entry::getKey) .filter(e -> e.getValue().getAsString().equals(name) && validateResourceInModel(lwM2mClient, e.getKey(), false)).findFirst().map(Map.Entry::getKey)
.orElse(null); .orElse(null);
@ -1256,7 +1232,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @param sessionInfo * @param sessionInfo
*/ */
public void updateAttributeFromThingsboard(List<TransportProtos.TsKvProto> tsKvProtos, TransportProtos.SessionInfoProto sessionInfo) { public void updateAttributeFromThingsboard(List<TransportProtos.TsKvProto> tsKvProtos, TransportProtos.SessionInfoProto sessionInfo) {
LwM2mClient lwM2MClient = clientContext.getClient(sessionInfo); LwM2mClient lwM2MClient = clientContext.getClientBySessionInfo(sessionInfo);
if (lwM2MClient != null) { if (lwM2MClient != null) {
log.warn("1) UpdateAttributeFromThingsboard, tsKvProtos [{}]", tsKvProtos); log.warn("1) UpdateAttributeFromThingsboard, tsKvProtos [{}]", tsKvProtos);
tsKvProtos.forEach(tsKvProto -> { tsKvProtos.forEach(tsKvProto -> {
@ -1275,8 +1251,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer),
getValueFromKvProto(tsKvProto.getKv()), pathIdVer); getValueFromKvProto(tsKvProto.getKv()), pathIdVer);
}); });
} } else {
else {
log.error("UpdateAttributeFromThingsboard, lwM2MClient is null"); log.error("UpdateAttributeFromThingsboard, lwM2MClient is null");
} }
} }
@ -1285,14 +1260,9 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @param lwM2MClient - * @param lwM2MClient -
* @return SessionInfoProto - * @return SessionInfoProto -
*/ */
private SessionInfoProto getSessionInfoOrCloseSession(LwM2mClient lwM2MClient) { private SessionInfoProto getSessionInfo(LwM2mClient lwM2MClient) {
if (lwM2MClient != null) { if (lwM2MClient != null && lwM2MClient.getSession() != null) {
SessionInfoProto sessionInfoProto = lwM2MClient.getSession(); return lwM2MClient.getSession();
if (sessionInfoProto == null) {
log.info("[{}] [{}]", lwM2MClient.getEndpoint(), CLIENT_NOT_AUTHORIZED);
this.closeClientSession(lwM2MClient.getRegistration());
}
return sessionInfoProto;
} }
return null; return null;
} }
@ -1302,15 +1272,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @return - sessionInfo after access connect client * @return - sessionInfo after access connect client
*/ */
public SessionInfoProto getSessionInfoOrCloseSession(Registration registration) { public SessionInfoProto getSessionInfoOrCloseSession(Registration registration) {
return getSessionInfoOrCloseSession(clientContext.getOrRegister(registration)); return getSessionInfo(clientContext.getClientByEndpoint(registration.getEndpoint()));
}
/**
* @param registrationId -
* @return -
*/
private SessionInfoProto getSessionInfoOrCloseSession(String registrationId) {
return getSessionInfoOrCloseSession(clientContext.getClientByRegistrationId(registrationId));
} }
/** /**
@ -1340,7 +1302,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @param lwM2MClient - LwM2M Client * @param lwM2MClient - LwM2M Client
*/ */
public void putDelayedUpdateResourcesThingsboard(LwM2mClient lwM2MClient) { public void putDelayedUpdateResourcesThingsboard(LwM2mClient lwM2MClient) {
SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient); SessionInfoProto sessionInfo = this.getSessionInfo(lwM2MClient);
if (sessionInfo != null) { if (sessionInfo != null) {
//#1.1 //#1.1
ConcurrentMap<String, String> keyNamesMap = this.getNamesFromProfileForSharedAttributes(lwM2MClient); ConcurrentMap<String, String> keyNamesMap = this.getNamesFromProfileForSharedAttributes(lwM2MClient);
@ -1357,9 +1319,9 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
} }
} }
public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient, Lwm2mClientRpcRequest rpcRequest) { public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient, LwM2mClientRpcRequest rpcRequest) {
if (lwM2MClient.getRegistration().getSupportedVersion(FW_ID) != null) { if (lwM2MClient.getRegistration().getSupportedVersion(FW_5_ID) != null) {
SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient); SessionInfoProto sessionInfo = this.getSessionInfo(lwM2MClient);
if (sessionInfo != null) { if (sessionInfo != null) {
DefaultLwM2MTransportMsgHandler handler = this; DefaultLwM2MTransportMsgHandler handler = this;
this.transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.FIRMWARE.name()), this.transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.FIRMWARE.name()),
@ -1368,19 +1330,33 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) { public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) {
if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())
&& response.getType().equals(OtaPackageType.FIRMWARE.name())) { && response.getType().equals(OtaPackageType.FIRMWARE.name())) {
log.warn ("7) firmware start with ver: [{}]", response.getVersion()); LwM2mFwSwUpdate fwUpdate = lwM2MClient.getFwUpdate(clientContext);
lwM2MClient.getFwUpdate().setRpcRequest(rpcRequest); if (rpcRequest != null) {
lwM2MClient.getFwUpdate().setCurrentVersion(response.getVersion()); fwUpdate.setStateUpdate(INITIATED.name());
lwM2MClient.getFwUpdate().setCurrentTitle(response.getTitle());
lwM2MClient.getFwUpdate().setCurrentId(new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())).getId());
if (rpcRequest == null) {
lwM2MClient.getFwUpdate().sendReadObserveInfo(lwM2mTransportRequest);
} }
else { if (!FAILED.name().equals(fwUpdate.getStateUpdate())) {
lwM2MClient.getFwUpdate().writeFwSwWare(handler, lwM2mTransportRequest); log.warn("7) firmware start with ver: [{}]", response.getVersion());
fwUpdate.setRpcRequest(rpcRequest);
fwUpdate.setCurrentVersion(response.getVersion());
fwUpdate.setCurrentTitle(response.getTitle());
fwUpdate.setCurrentId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB()));
if (rpcRequest == null) {
fwUpdate.sendReadObserveInfo(lwM2mTransportRequest);
} else {
fwUpdate.writeFwSwWare(handler, lwM2mTransportRequest);
}
} else {
String msgError = String.format("OtaPackage device: %s, version: %s, stateUpdate: %s",
lwM2MClient.getDeviceName(), response.getVersion(), fwUpdate.getStateUpdate());
log.warn("7_1 [{}]", msgError);
} }
} else { } else {
log.trace("OtaPackage [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); String msgError = String.format("OtaPackage device: %s, responseStatus: %s",
lwM2MClient.getDeviceName(), response.getResponseStatus().toString());
log.trace(msgError);
if (rpcRequest != null) {
sendErrorRpcResponse(rpcRequest, msgError, sessionInfo);
}
} }
} }
@ -1393,9 +1369,9 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
} }
} }
public void getInfoSoftwareUpdate(LwM2mClient lwM2MClient, Lwm2mClientRpcRequest rpcRequest) { public void getInfoSoftwareUpdate(LwM2mClient lwM2MClient, LwM2mClientRpcRequest rpcRequest) {
if (lwM2MClient.getRegistration().getSupportedVersion(SW_ID) != null) { if (lwM2MClient.getRegistration().getSupportedVersion(SW_ID) != null) {
SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient); SessionInfoProto sessionInfo = this.getSessionInfo(lwM2MClient);
if (sessionInfo != null) { if (sessionInfo != null) {
DefaultLwM2MTransportMsgHandler handler = this; DefaultLwM2MTransportMsgHandler handler = this;
transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.SOFTWARE.name()), transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.SOFTWARE.name()),
@ -1411,9 +1387,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest); lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest);
if (rpcRequest == null) { if (rpcRequest == null) {
lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest); lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest);
} } else {
else { lwM2MClient.getSwUpdate().writeFwSwWare(handler, lwM2mTransportRequest);
lwM2MClient.getSwUpdate().writeFwSwWare(handler, lwM2mTransportRequest);
} }
} else { } else {
log.trace("Software [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); log.trace("Software [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString());

23
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java

@ -28,7 +28,6 @@ import org.eclipse.leshan.server.californium.registration.CaliforniumRegistratio
import org.eclipse.leshan.server.model.LwM2mModelProvider; import org.eclipse.leshan.server.model.LwM2mModelProvider;
import org.eclipse.leshan.server.security.EditableSecurityStore; import org.eclipse.leshan.server.security.EditableSecurityStore;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
@ -36,6 +35,8 @@ import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MAuthorizer; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MAuthorizer;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MDtlsCertificateVerifier; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MDtlsCertificateVerifier;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.server.store.TbEditableSecurityStore;
import org.thingsboard.server.transport.lwm2m.server.store.TbSecurityStore;
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@ -58,14 +59,13 @@ import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec; import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256;
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_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_CBC_SHA256;
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig; import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RECOURSE;
@Slf4j @Slf4j
@Component @Component
@ -81,9 +81,9 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
private final LwM2mTransportContext context; private final LwM2mTransportContext context;
private final LwM2MTransportServerConfig config; private final LwM2MTransportServerConfig config;
private final LwM2mTransportServerHelper helper; private final LwM2mTransportServerHelper helper;
private final LwM2mTransportMsgHandler handler; private final DefaultLwM2MTransportMsgHandler handler;
private final CaliforniumRegistrationStore registrationStore; private final CaliforniumRegistrationStore registrationStore;
private final EditableSecurityStore securityStore; private final TbSecurityStore securityStore;
private final LwM2mClientContext lwM2mClientContext; private final LwM2mClientContext lwM2mClientContext;
private final TbLwM2MDtlsCertificateVerifier certificateVerifier; private final TbLwM2MDtlsCertificateVerifier certificateVerifier;
private final TbLwM2MAuthorizer authorizer; private final TbLwM2MAuthorizer authorizer;
@ -96,6 +96,17 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
new LWM2MGenerationPSkRPkECC(); new LWM2MGenerationPSkRPkECC();
} }
this.server = getLhServer(); this.server = getLhServer();
/**
* Add a resource to the server.
* CoapResource ->
* path = FW_PACKAGE or SW_PACKAGE
* nameFile = "BC68JAR01A09_TO_BC68JAR01A10.bin"
* "coap://host:port/{path}/{token}/{nameFile}"
*/
LwM2mTransportCoapResource otaCoapResource = new LwM2mTransportCoapResource(handler, FIRMWARE_UPDATE_COAP_RECOURSE);
this.server.coap().getServer().add(otaCoapResource);
this.startLhServer(); this.startLhServer();
this.context.setServer(server); this.context.setServer(server);
} }
@ -126,7 +137,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance())); builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance()));
/* Create CoAP Config */ /* Create CoAP Config */
builder.setCoapConfig(getCoapConfig(config.getPort(), config.getSecurePort())); builder.setCoapConfig(getCoapConfig(config.getPort(), config.getSecurePort(), config));
/* Define model provider (Create Models )*/ /* Define model provider (Create Models )*/
LwM2mModelProvider modelProvider = new LwM2mVersionedModelProvider(this.lwM2mClientContext, this.helper, this.context); LwM2mModelProvider modelProvider = new LwM2mVersionedModelProvider(this.lwM2mClientContext, this.helper, this.context);

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

@ -16,10 +16,14 @@
package org.thingsboard.server.transport.lwm2m.server; package org.thingsboard.server.transport.lwm2m.server;
import org.eclipse.californium.core.network.config.NetworkConfig; import org.eclipse.californium.core.network.config.NetworkConfig;
import org.eclipse.californium.core.network.config.NetworkConfigDefaults;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import static org.eclipse.californium.core.network.config.NetworkConfigDefaults.DEFAULT_BLOCKWISE_STATUS_LIFETIME;
public class LwM2mNetworkConfig { public class LwM2mNetworkConfig {
public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort) { public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort, LwM2MTransportServerConfig config) {
NetworkConfig coapConfig = new NetworkConfig(); NetworkConfig coapConfig = new NetworkConfig();
coapConfig.setInt(NetworkConfig.Keys.COAP_PORT,serverPortNoSec); coapConfig.setInt(NetworkConfig.Keys.COAP_PORT,serverPortNoSec);
coapConfig.setInt(NetworkConfig.Keys.COAP_SECURE_PORT,serverSecurePort); coapConfig.setInt(NetworkConfig.Keys.COAP_SECURE_PORT,serverSecurePort);
@ -49,8 +53,15 @@ public class LwM2mNetworkConfig {
- value of true indicate that the server will response with block2 option event 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.setBoolean(NetworkConfig.Keys.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true);
/**
coapConfig.setInt(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, 300000); * The maximum amount of time (in milliseconds) allowed between
* transfers of individual blocks in a blockwise transfer before the
* blockwise transfer state is discarded.
* <p>
* The default value of this property is
* {@link NetworkConfigDefaults#DEFAULT_BLOCKWISE_STATUS_LIFETIME} = 5 * 60 * 1000; // 5 mins [ms].
*/
coapConfig.setLong(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, DEFAULT_BLOCKWISE_STATUS_LIFETIME);
/** /**
!!! REQUEST_ENTITY_TOO_LARGE CODE=4.13 !!! REQUEST_ENTITY_TOO_LARGE CODE=4.13
The maximum size of a resource body (in bytes) that will be accepted The maximum size of a resource body (in bytes) that will be accepted

25
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOtaConvert.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.transport.lwm2m.server;
import lombok.Data;
import org.eclipse.leshan.core.model.ResourceModel;
@Data
public class LwM2mOtaConvert {
private ResourceModel.Type currentType;
private Object value;
}

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

@ -87,7 +87,7 @@ public class LwM2mServerListener {
@Override @Override
public void cancelled(Observation observation) { public void cancelled(Observation observation) {
String msg = String.format("%s: Canceled Observation %s.", LOG_LW2M_INFO, observation.getPath()); String msg = String.format("%s: Canceled Observation %s.", LOG_LW2M_INFO, observation.getPath());
service.sendLogsToThingsboard(msg, observation.getRegistrationId()); service.sendLogsToThingsboard(observation.getRegistrationId(), msg);
log.warn(msg); log.warn(msg);
} }
@ -109,7 +109,7 @@ public class LwM2mServerListener {
String msg = String.format("%s: Successful start newObservation %s.", LOG_LW2M_INFO, String msg = String.format("%s: Successful start newObservation %s.", LOG_LW2M_INFO,
observation.getPath()); observation.getPath());
log.warn(msg); log.warn(msg);
service.sendLogsToThingsboard(msg, registration.getId()); service.sendLogsToThingsboard(registration.getId(), msg);
} }
}; };
} }

158
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java

@ -0,0 +1,158 @@
/**
* 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.extern.slf4j.Slf4j;
import org.eclipse.californium.core.coap.CoAP;
import org.eclipse.californium.core.coap.Request;
import org.eclipse.californium.core.coap.Response;
import org.eclipse.californium.core.network.Exchange;
import org.eclipse.californium.core.observe.ObserveRelation;
import org.eclipse.californium.core.server.resources.CoapExchange;
import org.eclipse.californium.core.server.resources.Resource;
import org.eclipse.californium.core.server.resources.ResourceObserver;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RECOURSE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SOFTWARE_UPDATE_COAP_RECOURSE;
@Slf4j
public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource {
private final ConcurrentMap<String, ObserveRelation> tokenToObserveRelationMap = new ConcurrentHashMap<>();
private final ConcurrentMap<String, AtomicInteger> tokenToObserveNotificationSeqMap = new ConcurrentHashMap<>();
private final LwM2mTransportMsgHandler handler;
public LwM2mTransportCoapResource(LwM2mTransportMsgHandler handler, String name) {
super(name);
this.handler = handler;
this.setObservable(true); // enable observing
this.addObserver(new CoapResourceObserver());
}
@Override
public void checkObserveRelation(Exchange exchange, Response response) {
String token = getTokenFromRequest(exchange.getRequest());
final ObserveRelation relation = exchange.getRelation();
if (relation == null || relation.isCanceled()) {
return; // because request did not try to establish a relation
}
if (CoAP.ResponseCode.isSuccess(response.getCode())) {
if (!relation.isEstablished()) {
relation.setEstablished();
addObserveRelation(relation);
}
AtomicInteger notificationCounter = tokenToObserveNotificationSeqMap.computeIfAbsent(token, s -> new AtomicInteger(0));
response.getOptions().setObserve(notificationCounter.getAndIncrement());
} // ObserveLayer takes care of the else case
}
@Override
protected void processHandleGet(CoapExchange exchange) {
log.warn("90) processHandleGet [{}]", exchange);
if (exchange.getRequestOptions().getUriPath().size() >= 2 &&
(FIRMWARE_UPDATE_COAP_RECOURSE.equals(exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size()-2)) ||
SOFTWARE_UPDATE_COAP_RECOURSE.equals(exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size()-2)))) {
this.sendOtaData(exchange);
}
}
@Override
protected void processHandlePost(CoapExchange exchange) {
log.warn("2) processHandleGet [{}]", exchange);
}
/**
* Override the default behavior so that requests to sub resources (typically /{name}/{token}) are handled by
* /name resource.
*/
@Override
public Resource getChild(String name) {
return this;
}
private String getTokenFromRequest(Request request) {
return (request.getSourceContext() != null ? request.getSourceContext().getPeerAddress().getAddress().getHostAddress() : "null")
+ ":" + (request.getSourceContext() != null ? request.getSourceContext().getPeerAddress().getPort() : -1) + ":" + request.getTokenString();
}
public class CoapResourceObserver implements ResourceObserver {
@Override
public void changedName(String old) {
}
@Override
public void changedPath(String old) {
}
@Override
public void addedChild(Resource child) {
}
@Override
public void removedChild(Resource child) {
}
@Override
public void addedObserveRelation(ObserveRelation relation) {
}
@Override
public void removedObserveRelation(ObserveRelation relation) {
}
}
private void sendOtaData(CoapExchange exchange) {
String idStr = exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size()-1
);
UUID currentId = UUID.fromString(idStr);
Response response = new Response(CoAP.ResponseCode.CONTENT);
byte[] fwData = this.getOtaData(currentId);
log.warn("91) read softWare data (length): [{}]", fwData.length);
if (fwData != null && fwData.length > 0) {
response.setPayload(fwData);
if (exchange.getRequestOptions().getBlock2() != null) {
int chunkSize = exchange.getRequestOptions().getBlock2().getSzx();
boolean moreFlag = fwData.length > chunkSize;
response.getOptions().setBlock2(chunkSize, moreFlag, 0);
log.warn("92) with blokc2 Send currentId: [{}], length: [{}], chunkSize [{}], moreFlag [{}]", currentId.toString(), fwData.length, chunkSize, moreFlag);
}
else {
log.warn("92) with block1 Send currentId: [{}], length: [{}], ", currentId.toString(), fwData.length);
}
exchange.respond(response);
}
}
private byte[] getOtaData(UUID currentId) {
return ((DefaultLwM2MTransportMsgHandler) handler).otaPackageDataCache.get(currentId.toString());
}
}

11
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportMsgHandler.java

@ -22,7 +22,8 @@ import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientRpcRequest;
import java.util.Collection; import java.util.Collection;
import java.util.Optional; import java.util.Optional;
@ -39,7 +40,7 @@ public interface LwM2mTransportMsgHandler {
void setCancelObservationsAll(Registration registration); void setCancelObservationsAll(Registration registration);
void onUpdateValueAfterReadResponse(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); void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo);
@ -57,13 +58,13 @@ public interface LwM2mTransportMsgHandler {
void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse); void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse);
void doTrigger(Registration registration, String path);
void doDisconnect(TransportProtos.SessionInfoProto sessionInfo); void doDisconnect(TransportProtos.SessionInfoProto sessionInfo);
void onAwakeDev(Registration registration); void onAwakeDev(Registration registration);
void sendLogsToThingsboard(String msg, String registrationId); void sendLogsToThingsboard(LwM2mClient client, String msg);
void sendLogsToThingsboard(String registrationId, String msg);
LwM2MTransportServerConfig getConfig(); LwM2MTransportServerConfig getConfig();
} }

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

@ -32,10 +32,10 @@ import org.eclipse.leshan.core.observation.Observation;
import org.eclipse.leshan.core.request.ContentFormat; import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.core.request.DeleteRequest; import org.eclipse.leshan.core.request.DeleteRequest;
import org.eclipse.leshan.core.request.DiscoverRequest; import org.eclipse.leshan.core.request.DiscoverRequest;
import org.eclipse.leshan.core.request.DownlinkRequest;
import org.eclipse.leshan.core.request.ExecuteRequest; import org.eclipse.leshan.core.request.ExecuteRequest;
import org.eclipse.leshan.core.request.ObserveRequest; import org.eclipse.leshan.core.request.ObserveRequest;
import org.eclipse.leshan.core.request.ReadRequest; import org.eclipse.leshan.core.request.ReadRequest;
import org.eclipse.leshan.core.request.SimpleDownlinkRequest;
import org.eclipse.leshan.core.request.WriteAttributesRequest; import org.eclipse.leshan.core.request.WriteAttributesRequest;
import org.eclipse.leshan.core.request.WriteRequest; import org.eclipse.leshan.core.request.WriteRequest;
import org.eclipse.leshan.core.request.exception.ClientSleepingException; import org.eclipse.leshan.core.request.exception.ClientSleepingException;
@ -55,7 +55,7 @@ import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; 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.LwM2mClient;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientRpcRequest;
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@ -75,7 +75,7 @@ import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWN
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getContentFormatByResourceModelType; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getContentFormatByResourceModelType;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEFAULT_TIMEOUT; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEFAULT_TIMEOUT;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_5_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO;
@ -116,34 +116,32 @@ public class LwM2mTransportRequest {
new NamedThreadFactory(String.format("LwM2M %s channel response after request", RESPONSE_REQUEST_CHANNEL))); new NamedThreadFactory(String.format("LwM2M %s channel response after request", RESPONSE_REQUEST_CHANNEL)));
} }
/** public void sendAllRequest(LwM2mClient lwM2MClient, String targetIdVer, LwM2mTypeOper typeOper, Object params, long timeoutInMs, LwM2mClientRpcRequest lwm2mClientRpcRequest) {
* Device management and service enablement, including Read, Write, Execute, Discover, Create, Delete and Write-Attributes sendAllRequest(lwM2MClient, targetIdVer, typeOper, lwM2MClient.getDefaultContentFormat(), params, timeoutInMs, lwm2mClientRpcRequest);
* }
* @param registration -
* @param targetIdVer -
* @param typeOper -
* @param contentFormatName -
*/
public void sendAllRequest(Registration registration, String targetIdVer, LwM2mTypeOper typeOper,
String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest lwm2mClientRpcRequest) { public void sendAllRequest(LwM2mClient lwM2MClient, String targetIdVer, LwM2mTypeOper typeOper,
ContentFormat contentFormat, Object params, long timeoutInMs, LwM2mClientRpcRequest lwm2mClientRpcRequest) {
Registration registration = lwM2MClient.getRegistration();
try { try {
String target = convertPathFromIdVerToObjectId(targetIdVer); String target = convertPathFromIdVerToObjectId(targetIdVer);
ContentFormat contentFormat = contentFormatName != null ? ContentFormat.fromName(contentFormatName.toUpperCase()) : ContentFormat.DEFAULT; if(contentFormat == null){
LwM2mClient lwM2MClient = this.lwM2mClientContext.getOrRegister(registration); contentFormat = ContentFormat.DEFAULT;
}
LwM2mPath resultIds = target != null ? new LwM2mPath(target) : null; LwM2mPath resultIds = target != null ? new LwM2mPath(target) : null;
if (!OBSERVE_CANCEL.name().equals(typeOper.name()) && resultIds != null && registration != null && resultIds.getObjectId() >= 0 && lwM2MClient != null) { if (!OBSERVE_CANCEL.name().equals(typeOper.name()) && resultIds != null && registration != null && resultIds.getObjectId() >= 0 && lwM2MClient != null) {
if (lwM2MClient.isValidObjectVersion(targetIdVer)) { if (lwM2MClient.isValidObjectVersion(targetIdVer)) {
timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT; timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT;
DownlinkRequest request = createRequest(registration, lwM2MClient, typeOper, contentFormat, target, SimpleDownlinkRequest request = createRequest(registration, lwM2MClient, typeOper, contentFormat, target,
targetIdVer, resultIds, params, lwm2mClientRpcRequest); targetIdVer, resultIds, params, lwm2mClientRpcRequest);
if (request != null) { if (request != null) {
try { try {
this.sendRequest(registration, lwM2MClient, request, timeoutInMs, lwm2mClientRpcRequest); this.sendRequest(registration, lwM2MClient, request, timeoutInMs, lwm2mClientRpcRequest);
} catch (ClientSleepingException e) { } catch (ClientSleepingException e) {
DownlinkRequest finalRequest = request; SimpleDownlinkRequest finalRequest = request;
long finalTimeoutInMs = timeoutInMs; long finalTimeoutInMs = timeoutInMs;
Lwm2mClientRpcRequest finalRpcRequest = lwm2mClientRpcRequest; LwM2mClientRpcRequest finalRpcRequest = lwm2mClientRpcRequest;
lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, finalRpcRequest)); lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, finalRpcRequest));
} catch (Exception e) { } catch (Exception e) {
log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper.name(), e); log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper.name(), e);
@ -163,12 +161,12 @@ public class LwM2mTransportRequest {
if (lwm2mClientRpcRequest != null) { if (lwm2mClientRpcRequest != null) {
ResourceModel resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider()); ResourceModel resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider());
String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null"; String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null";
this.handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
} }
} }
} else if (lwm2mClientRpcRequest != null) { } else if (lwm2mClientRpcRequest != null) {
String errorMsg = String.format("Path %s not found in object version", targetIdVer); String errorMsg = String.format("Path %s not found in object version", targetIdVer);
this.handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
} }
} else { } else {
switch (typeOper) { switch (typeOper) {
@ -185,10 +183,10 @@ public class LwM2mTransportRequest {
} }
String msg = String.format("%s: type operation %s paths - %s", LOG_LW2M_INFO, String msg = String.format("%s: type operation %s paths - %s", LOG_LW2M_INFO,
typeOper.name(), paths); typeOper.name(), paths);
this.handler.sendLogsToThingsboard(msg, registration.getId()); this.handler.sendLogsToThingsboard(lwM2MClient, msg);
if (lwm2mClientRpcRequest != null) { if (lwm2mClientRpcRequest != null) {
String valueMsg = String.format("Paths - %s", paths); String valueMsg = String.format("Paths - %s", paths);
this.handler.sentRpcResponse(lwm2mClientRpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE); handler.sentRpcResponse(lwm2mClientRpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE);
} }
break; break;
case OBSERVE_CANCEL: case OBSERVE_CANCEL:
@ -204,18 +202,18 @@ public class LwM2mTransportRequest {
observeCancelMsg = String.format("%s: type operation %s paths: All count: %d", LOG_LW2M_INFO, observeCancelMsg = String.format("%s: type operation %s paths: All count: %d", LOG_LW2M_INFO,
OBSERVE_CANCEL.name(), observeCancelCnt); OBSERVE_CANCEL.name(), observeCancelCnt);
} }
this.afterObserveCancel(registration, observeCancelCnt, observeCancelMsg, lwm2mClientRpcRequest); this.afterObserveCancel(lwM2MClient, observeCancelCnt, observeCancelMsg, lwm2mClientRpcRequest);
break; break;
// lwm2mClientRpcRequest != null // lwm2mClientRpcRequest != null
case FW_UPDATE: case FW_UPDATE:
this.handler.getInfoFirmwareUpdate(lwM2MClient, lwm2mClientRpcRequest); handler.getInfoFirmwareUpdate(lwM2MClient, lwm2mClientRpcRequest);
break; break;
} }
} }
} catch (Exception e) { } catch (Exception e) {
String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR, String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR,
typeOper.name(), e.getMessage()); typeOper.name(), e.getMessage());
handler.sendLogsToThingsboard(msg, registration.getId()); handler.sendLogsToThingsboard(lwM2MClient, msg);
if (lwm2mClientRpcRequest != null) { if (lwm2mClientRpcRequest != null) {
String errorMsg = String.format("Path %s type operation %s %s", targetIdVer, typeOper.name(), e.getMessage()); String errorMsg = String.format("Path %s type operation %s %s", targetIdVer, typeOper.name(), e.getMessage());
handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
@ -223,10 +221,10 @@ public class LwM2mTransportRequest {
} }
} }
private DownlinkRequest createRequest(Registration registration, LwM2mClient lwM2MClient, LwM2mTypeOper typeOper, private SimpleDownlinkRequest createRequest(Registration registration, LwM2mClient lwM2MClient, LwM2mTypeOper typeOper,
ContentFormat contentFormat, String target, String targetIdVer, ContentFormat contentFormat, String target, String targetIdVer,
LwM2mPath resultIds, Object params, Lwm2mClientRpcRequest rpcRequest) { LwM2mPath resultIds, Object params, LwM2mClientRpcRequest rpcRequest) {
DownlinkRequest request = null; SimpleDownlinkRequest request = null;
switch (typeOper) { switch (typeOper) {
case READ: case READ:
request = new ReadRequest(contentFormat, target); request = new ReadRequest(contentFormat, target);
@ -273,7 +271,7 @@ public class LwM2mTransportRequest {
contentFormat = getContentFormatByResourceModelType(resourceModelWrite, contentFormat); contentFormat = getContentFormatByResourceModelType(resourceModelWrite, contentFormat);
request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(), request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(),
resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModelWrite.type, resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModelWrite.type,
registration, rpcRequest); lwM2MClient, rpcRequest);
} }
break; break;
case WRITE_UPDATE: case WRITE_UPDATE:
@ -329,19 +327,19 @@ public class LwM2mTransportRequest {
*/ */
@SuppressWarnings({"error sendRequest"}) @SuppressWarnings({"error sendRequest"})
private void sendRequest(Registration registration, LwM2mClient lwM2MClient, DownlinkRequest request, private void sendRequest(Registration registration, LwM2mClient lwM2MClient, SimpleDownlinkRequest request,
long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) { long timeoutInMs, LwM2mClientRpcRequest rpcRequest) {
context.getServer().send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> { context.getServer().send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> {
if (!lwM2MClient.isInit()) { if (!lwM2MClient.isInit()) {
lwM2MClient.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); lwM2MClient.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
} }
if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) { if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) {
this.handleResponse(registration, request.getPath().toString(), response, request, rpcRequest); this.handleResponse(lwM2MClient, request.getPath().toString(), response, request, rpcRequest);
} else { } else {
String msg = String.format("%s: SendRequest %s: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s", LOG_LW2M_ERROR, request.getClass().getName().toString(), 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()); ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString());
handler.sendLogsToThingsboard(msg, registration.getId()); handler.sendLogsToThingsboard(lwM2MClient, msg);
log.error("[{}] [{}], [{}] - [{}] [{}] error SendRequest", request.getClass().getName().toString(), registration.getEndpoint(), log.error("[{}] [{}], [{}] - [{}] [{}] error SendRequest", request.getClass().getName().toString(), registration.getEndpoint(),
((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString()); ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString());
if (!lwM2MClient.isInit()) { if (!lwM2MClient.isInit()) {
@ -354,13 +352,13 @@ public class LwM2mTransportRequest {
/** Not Found /** Not Found
set setClient_fw_info... = empty set setClient_fw_info... = empty
**/ **/
if (lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) { if (lwM2MClient.getFwUpdate() != null && lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) {
lwM2MClient.getFwUpdate().initReadValue(handler, this, request.getPath().toString()); lwM2MClient.getFwUpdate().initReadValue(handler, this, request.getPath().toString());
} }
if (lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) { if (lwM2MClient.getSwUpdate() != null && lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) {
lwM2MClient.getSwUpdate().initReadValue(handler, this, request.getPath().toString()); lwM2MClient.getSwUpdate().initReadValue(handler, this, request.getPath().toString());
} }
if (request.getPath().toString().equals(FW_PACKAGE_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { if (request.getPath().toString().equals(FW_PACKAGE_5_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) {
this.afterWriteFwSWUpdateError(registration, request, response.getErrorMessage()); this.afterWriteFwSWUpdateError(registration, request, response.getErrorMessage());
} }
if (request.getPath().toString().equals(FW_UPDATE_ID) || request.getPath().toString().equals(SW_INSTALL_ID)) { if (request.getPath().toString().equals(FW_UPDATE_ID) || request.getPath().toString().equals(SW_INSTALL_ID)) {
@ -371,13 +369,13 @@ public class LwM2mTransportRequest {
/** version == null /** version == null
set setClient_fw_info... = empty set setClient_fw_info... = empty
**/ **/
if (lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) { if (lwM2MClient.getFwUpdate() != null && lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) {
lwM2MClient.getFwUpdate().initReadValue(handler, this, request.getPath().toString()); lwM2MClient.getFwUpdate().initReadValue(handler, this, request.getPath().toString());
} }
if (lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) { if (lwM2MClient.getSwUpdate() != null && lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) {
lwM2MClient.getSwUpdate().initReadValue(handler, this, request.getPath().toString()); lwM2MClient.getSwUpdate().initReadValue(handler, this, request.getPath().toString());
} }
if (request.getPath().toString().equals(FW_PACKAGE_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { if (request.getPath().toString().equals(FW_PACKAGE_5_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) {
this.afterWriteFwSWUpdateError(registration, request, e.getMessage()); this.afterWriteFwSWUpdateError(registration, request, e.getMessage());
} }
if (request.getPath().toString().equals(FW_UPDATE_ID) || request.getPath().toString().equals(SW_INSTALL_ID)) { if (request.getPath().toString().equals(FW_UPDATE_ID) || request.getPath().toString().equals(SW_INSTALL_ID)) {
@ -388,7 +386,7 @@ public class LwM2mTransportRequest {
} }
String msg = String.format("%s: SendRequest %s: Resource path - %s msg error - %s", 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()); LOG_LW2M_ERROR, request.getClass().getName().toString(), request.getPath().toString(), e.getMessage());
handler.sendLogsToThingsboard(msg, registration.getId()); handler.sendLogsToThingsboard(lwM2MClient, msg);
log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString()); log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString());
if (rpcRequest != null) { if (rpcRequest != null) {
handler.sentRpcResponse(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR); handler.sentRpcResponse(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR);
@ -398,7 +396,7 @@ public class LwM2mTransportRequest {
private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId, private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId,
Integer resourceId, Object value, ResourceModel.Type type, Integer resourceId, Object value, ResourceModel.Type type,
Registration registration, Lwm2mClientRpcRequest rpcRequest) { LwM2mClient client, LwM2mClientRpcRequest rpcRequest) {
try { try {
if (type != null) { if (type != null) {
switch (type) { switch (type) {
@ -433,7 +431,7 @@ public class LwM2mTransportRequest {
String patn = "/" + objectId + "/" + instanceId + "/" + resourceId; 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", 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()); patn, type, value, e.toString());
handler.sendLogsToThingsboard(msg, registration.getId()); handler.sendLogsToThingsboard(client, msg);
log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString()); log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString());
if (rpcRequest != null) { if (rpcRequest != null) {
String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value); String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value);
@ -443,13 +441,13 @@ public class LwM2mTransportRequest {
} }
} }
private void handleResponse(Registration registration, final String path, LwM2mResponse response, private void handleResponse(LwM2mClient lwM2mClient, final String path, LwM2mResponse response,
DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) { SimpleDownlinkRequest request, LwM2mClientRpcRequest rpcRequest) {
responseRequestExecutor.submit(() -> { responseRequestExecutor.submit(() -> {
try { try {
this.sendResponse(registration, path, response, request, rpcRequest); this.sendResponse(lwM2mClient, path, response, request, rpcRequest);
} catch (Exception e) { } catch (Exception e) {
log.error("[{}] endpoint [{}] path [{}] Exception Unable to after send response.", registration.getEndpoint(), path, e); log.error("[{}] endpoint [{}] path [{}] Exception Unable to after send response.", lwM2mClient.getRegistration().getEndpoint(), path, e);
} }
}); });
} }
@ -457,12 +455,12 @@ public class LwM2mTransportRequest {
/** /**
* processing a response from a client * processing a response from a client
* *
* @param registration -
* @param path - * @param path -
* @param response - * @param response -
*/ */
private void sendResponse(Registration registration, String path, LwM2mResponse response, private void sendResponse(LwM2mClient lwM2mClient, String path, LwM2mResponse response,
DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) { SimpleDownlinkRequest request, LwM2mClientRpcRequest rpcRequest) {
Registration registration = lwM2mClient.getRegistration();
String pathIdVer = convertPathFromObjectIdToIdVer(path, registration); String pathIdVer = convertPathFromObjectIdToIdVer(path, registration);
String msgLog = ""; String msgLog = "";
if (response instanceof ReadResponse) { if (response instanceof ReadResponse) {
@ -477,7 +475,7 @@ public class LwM2mTransportRequest {
String discoverValue = Link.serialize(((DiscoverResponse) response).getObjectLinks()); String discoverValue = Link.serialize(((DiscoverResponse) response).getObjectLinks());
msgLog = String.format("%s: type operation: %s path: %s value: %s", msgLog = String.format("%s: type operation: %s path: %s value: %s",
LOG_LW2M_INFO, DISCOVER.name(), request.getPath().toString(), discoverValue); LOG_LW2M_INFO, DISCOVER.name(), request.getPath().toString(), discoverValue);
handler.sendLogsToThingsboard(msgLog, registration.getId()); handler.sendLogsToThingsboard(lwM2mClient, msgLog);
log.warn("DiscoverResponse: [{}]", (DiscoverResponse) response); log.warn("DiscoverResponse: [{}]", (DiscoverResponse) response);
if (rpcRequest != null) { if (rpcRequest != null) {
handler.sentRpcResponse(rpcRequest, response.getCode().getName(), discoverValue, LOG_LW2M_VALUE); handler.sentRpcResponse(rpcRequest, response.getCode().getName(), discoverValue, LOG_LW2M_VALUE);
@ -486,7 +484,7 @@ public class LwM2mTransportRequest {
msgLog = String.format("%s: type operation: %s path: %s", msgLog = String.format("%s: type operation: %s path: %s",
LOG_LW2M_INFO, EXECUTE.name(), request.getPath().toString()); LOG_LW2M_INFO, EXECUTE.name(), request.getPath().toString());
log.warn("9) [{}] ", msgLog); log.warn("9) [{}] ", msgLog);
handler.sendLogsToThingsboard(msgLog, registration.getId()); handler.sendLogsToThingsboard(lwM2mClient, msgLog);
if (rpcRequest != null) { if (rpcRequest != null) {
msgLog = String.format("Start %s path: %S. Preparation finished: %s", EXECUTE.name(), path, rpcRequest.getInfoMsg()); msgLog = String.format("Start %s path: %S. Preparation finished: %s", EXECUTE.name(), path, rpcRequest.getInfoMsg());
rpcRequest.setInfoMsg(msgLog); rpcRequest.setInfoMsg(msgLog);
@ -496,7 +494,7 @@ public class LwM2mTransportRequest {
} else if (response instanceof WriteAttributesResponse) { } else if (response instanceof WriteAttributesResponse) {
msgLog = String.format("%s: type operation: %s path: %s value: %s", msgLog = String.format("%s: type operation: %s path: %s value: %s",
LOG_LW2M_INFO, WRITE_ATTRIBUTES.name(), request.getPath().toString(), ((WriteAttributesRequest) request).getAttributes().toString()); LOG_LW2M_INFO, WRITE_ATTRIBUTES.name(), request.getPath().toString(), ((WriteAttributesRequest) request).getAttributes().toString());
handler.sendLogsToThingsboard(msgLog, registration.getId()); handler.sendLogsToThingsboard(lwM2mClient, msgLog);
log.warn("12) [{}] Path [{}] WriteAttributesResponse", pathIdVer, response); log.warn("12) [{}] Path [{}] WriteAttributesResponse", pathIdVer, response);
if (rpcRequest != null) { if (rpcRequest != null) {
handler.sentRpcResponse(rpcRequest, response.getCode().getName(), response.toString(), LOG_LW2M_VALUE); handler.sentRpcResponse(rpcRequest, response.getCode().getName(), response.toString(), LOG_LW2M_VALUE);
@ -504,13 +502,14 @@ public class LwM2mTransportRequest {
} else if (response instanceof WriteResponse) { } else if (response instanceof WriteResponse) {
msgLog = String.format("Type operation: Write path: %s", pathIdVer); msgLog = String.format("Type operation: Write path: %s", pathIdVer);
log.warn("10) [{}] response: [{}]", msgLog, response); log.warn("10) [{}] response: [{}]", msgLog, response);
this.infoWriteResponse(registration, response, request, rpcRequest); this.infoWriteResponse(lwM2mClient, response, request, rpcRequest);
handler.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request); handler.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request);
} }
} }
private void infoWriteResponse(Registration registration, LwM2mResponse response, DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) { private void infoWriteResponse(LwM2mClient lwM2mClient, LwM2mResponse response, SimpleDownlinkRequest request, LwM2mClientRpcRequest rpcRequest) {
try { try {
Registration registration = lwM2mClient.getRegistration();
LwM2mNode node = ((WriteRequest) request).getNode(); LwM2mNode node = ((WriteRequest) request).getNode();
String msg = null; String msg = null;
Object value; Object value;
@ -545,8 +544,8 @@ public class LwM2mTransportRequest {
} }
} }
if (msg != null) { if (msg != null) {
handler.sendLogsToThingsboard(msg, registration.getId()); handler.sendLogsToThingsboard(lwM2mClient, msg);
if (request.getPath().toString().equals(FW_PACKAGE_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { if (request.getPath().toString().equals(FW_PACKAGE_5_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) {
this.afterWriteSuccessFwSwUpdate(registration, request); this.afterWriteSuccessFwSwUpdate(registration, request);
if (rpcRequest != null) { if (rpcRequest != null) {
rpcRequest.setInfoMsg(msg); rpcRequest.setInfoMsg(msg);
@ -566,9 +565,9 @@ public class LwM2mTransportRequest {
* fw_state/sw_state = DOWNLOADED * fw_state/sw_state = DOWNLOADED
* send operation Execute * send operation Execute
*/ */
private void afterWriteSuccessFwSwUpdate(Registration registration, DownlinkRequest request) { private void afterWriteSuccessFwSwUpdate(Registration registration, SimpleDownlinkRequest request) {
LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
if (request.getPath().toString().equals(FW_PACKAGE_ID) && lwM2MClient.getFwUpdate() != null) { if (request.getPath().toString().equals(FW_PACKAGE_5_ID) && lwM2MClient.getFwUpdate() != null) {
lwM2MClient.getFwUpdate().setStateUpdate(DOWNLOADED.name()); lwM2MClient.getFwUpdate().setStateUpdate(DOWNLOADED.name());
lwM2MClient.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); lwM2MClient.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
} }
@ -581,9 +580,9 @@ public class LwM2mTransportRequest {
/** /**
* After finish operation FwSwUpdate Write (error): fw_state = FAILED * After finish operation FwSwUpdate Write (error): fw_state = FAILED
*/ */
private void afterWriteFwSWUpdateError(Registration registration, DownlinkRequest request, String msgError) { private void afterWriteFwSWUpdateError(Registration registration, SimpleDownlinkRequest request, String msgError) {
LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
if (request.getPath().toString().equals(FW_PACKAGE_ID) && lwM2MClient.getFwUpdate() != null) { if (request.getPath().toString().equals(FW_PACKAGE_5_ID) && lwM2MClient.getFwUpdate() != null) {
lwM2MClient.getFwUpdate().setStateUpdate(FAILED.name()); lwM2MClient.getFwUpdate().setStateUpdate(FAILED.name());
lwM2MClient.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); lwM2MClient.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError);
} }
@ -593,7 +592,7 @@ public class LwM2mTransportRequest {
} }
} }
private void afterExecuteFwSwUpdateError(Registration registration, DownlinkRequest request, String msgError) { private void afterExecuteFwSwUpdateError(Registration registration, SimpleDownlinkRequest request, String msgError) {
LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
if (request.getPath().toString().equals(FW_UPDATE_ID) && lwM2MClient.getFwUpdate() != null) { if (request.getPath().toString().equals(FW_UPDATE_ID) && lwM2MClient.getFwUpdate() != null) {
lwM2MClient.getFwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError); lwM2MClient.getFwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError);
@ -603,8 +602,8 @@ public class LwM2mTransportRequest {
} }
} }
private void afterObserveCancel(Registration registration, int observeCancelCnt, String observeCancelMsg, Lwm2mClientRpcRequest rpcRequest) { private void afterObserveCancel(LwM2mClient lwM2mClient, int observeCancelCnt, String observeCancelMsg, LwM2mClientRpcRequest rpcRequest) {
handler.sendLogsToThingsboard(observeCancelMsg, registration.getId()); handler.sendLogsToThingsboard(lwM2mClient, observeCancelMsg);
log.warn("[{}]", observeCancelMsg); log.warn("[{}]", observeCancelMsg);
if (rpcRequest != null) { if (rpcRequest != null) {
rpcRequest.setInfoMsg(String.format("Count: %d", observeCancelCnt)); rpcRequest.setInfoMsg(String.format("Count: %d", observeCancelCnt));

6
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java

@ -65,13 +65,11 @@ import static org.thingsboard.server.gen.transport.TransportProtos.KeyValueType.
public class LwM2mTransportServerHelper { public class LwM2mTransportServerHelper {
private final LwM2mTransportContext context; private final LwM2mTransportContext context;
private final LwM2MJsonAdaptor adaptor;
private final AtomicInteger atomicTs = new AtomicInteger(0); private final AtomicInteger atomicTs = new AtomicInteger(0);
public long getTS() { public long getTS() {
int addTs = atomicTs.getAndIncrement() >= 1000 ? atomicTs.getAndSet(0) : atomicTs.get(); return TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) * 1000L + (atomicTs.getAndIncrement() % 1000);
return TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) * 1000L + addTs;
} }
/** /**
@ -137,7 +135,7 @@ public class LwM2mTransportServerHelper {
public ObjectModel parseFromXmlToObjectModel(byte[] xmlByte, String streamName, DefaultDDFFileValidator ddfValidator) { public ObjectModel parseFromXmlToObjectModel(byte[] xmlByte, String streamName, DefaultDDFFileValidator ddfValidator) {
try { try {
DDFFileParser ddfFileParser = new DDFFileParser(ddfValidator); DDFFileParser ddfFileParser = new DDFFileParser(ddfValidator);
return ddfFileParser.parseEx(new ByteArrayInputStream(xmlByte), streamName).get(0); return ddfFileParser.parse(new ByteArrayInputStream(xmlByte), streamName).get(0);
} catch (IOException | InvalidDDFFileException e) { } catch (IOException | InvalidDDFFileException e) {
log.error("Could not parse the XML file [{}]", streamName, e); log.error("Could not parse the XML file [{}]", streamName, e);
return null; return null;

238
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java

@ -36,18 +36,18 @@ import org.eclipse.leshan.core.node.LwM2mObjectInstance;
import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.node.LwM2mSingleResource; import org.eclipse.leshan.core.node.LwM2mSingleResource;
import org.eclipse.leshan.core.node.codec.CodecException; import org.eclipse.leshan.core.node.codec.CodecException;
import org.eclipse.leshan.core.request.DownlinkRequest; import org.eclipse.leshan.core.request.SimpleDownlinkRequest;
import org.eclipse.leshan.core.request.WriteAttributesRequest; import org.eclipse.leshan.core.request.WriteAttributesRequest;
import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.core.util.Hex;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
import org.nustaq.serialization.FSTConfiguration; import org.nustaq.serialization.FSTConfiguration;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.ota.OtaPackageKey; import org.thingsboard.server.common.data.ota.OtaPackageKey;
import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus;
import org.thingsboard.server.common.data.ota.OtaPackageUtil; import org.thingsboard.server.common.data.ota.OtaPackageUtil;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile;
@ -77,18 +77,23 @@ import static org.eclipse.leshan.core.model.ResourceModel.Type.OBJLNK;
import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE;
import static org.eclipse.leshan.core.model.ResourceModel.Type.STRING; import static org.eclipse.leshan.core.model.ResourceModel.Type.STRING;
import static org.eclipse.leshan.core.model.ResourceModel.Type.TIME; import static org.eclipse.leshan.core.model.ResourceModel.Type.TIME;
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.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADING; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADING;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATED;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.VERIFIED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.VERIFIED;
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY;
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
@Slf4j @Slf4j
public class LwM2mTransportUtil { public class LwM2mTransportUtil {
public static final String EVENT_AWAKE = "AWAKE";
public static final String RESPONSE_REQUEST_CHANNEL = "RESP_REQ";
public static final String RESPONSE_CHANNEL = "RESP";
public static final String OBSERVE_CHANNEL = "OBSERVE";
public static final String TRANSPORT_DEFAULT_LWM2M_VERSION = "1.0"; public static final String TRANSPORT_DEFAULT_LWM2M_VERSION = "1.0";
public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings"; public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings";
public static final String BOOTSTRAP = "bootstrap"; public static final String BOOTSTRAP = "bootstrap";
@ -116,9 +121,6 @@ public class LwM2mTransportUtil {
public static final String LOG_LW2M_WARN = "warn"; public static final String LOG_LW2M_WARN = "warn";
public static final String LOG_LW2M_VALUE = "value"; public static final String LOG_LW2M_VALUE = "value";
public static final int LWM2M_STRATEGY_1 = 1;
public static final int LWM2M_STRATEGY_2 = 2;
public static final String CLIENT_NOT_AUTHORIZED = "Client not authorized"; public static final String CLIENT_NOT_AUTHORIZED = "Client not authorized";
public static final String LWM2M_VERSION_DEFAULT = "1.0"; public static final String LWM2M_VERSION_DEFAULT = "1.0";
@ -132,18 +134,23 @@ public class LwM2mTransportUtil {
public static final String FINISH_VALUE_KEY = ","; public static final String FINISH_VALUE_KEY = ",";
public static final String START_JSON_KEY = "{"; public static final String START_JSON_KEY = "{";
public static final String FINISH_JSON_KEY = "}"; public static final String FINISH_JSON_KEY = "}";
// public static final String contentFormatNameKey = "contentFormatName";
public static final String INFO_KEY = "info"; public static final String INFO_KEY = "info";
// public static final String TIME_OUT_IN_MS = "timeOutInMs";
public static final String RESULT_KEY = "result"; public static final String RESULT_KEY = "result";
public static final String ERROR_KEY = "error"; public static final String ERROR_KEY = "error";
public static final String METHOD_KEY = "methodName"; public static final String METHOD_KEY = "methodName";
// Firmware // Firmware
public static final String FIRMWARE_UPDATE_COAP_RECOURSE = "firmwareUpdateCoapRecourse";
public static final String FW_UPDATE = "Firmware update"; public static final String FW_UPDATE = "Firmware update";
public static final Integer FW_ID = 5; public static final Integer FW_5_ID = 5;
public static final Integer FW_19_ID = 19;
// Package W // Package W
public static final String FW_PACKAGE_ID = "/5/0/0"; public static final String FW_PACKAGE_5_ID = "/5/0/0";
public static final String FW_PACKAGE_19_ID = "/19/0/0";
// Package URI
public static final String FW_PACKAGE_URI_ID = "/5/0/1";
// State R // State R
public static final String FW_STATE_ID = "/5/0/3"; public static final String FW_STATE_ID = "/5/0/3";
// Update Result R // Update Result R
@ -151,15 +158,28 @@ public class LwM2mTransportUtil {
// PkgName R // PkgName R
public static final String FW_NAME_ID = "/5/0/6"; public static final String FW_NAME_ID = "/5/0/6";
// PkgVersion R // PkgVersion R
public static final String FW_VER_ID = "/5/0/7"; public static final String FW_5_VER_ID = "/5/0/7";
/**
* Quectel@Hi15RM1-HLB_V1.0@BC68JAR01A10,V150R100C20B300SP7,V150R100C20B300SP7@8
* BC68JAR01A10
* # Request prodct type number
* ATI
* Quectel
* BC68
* Revision:BC68JAR01A10
*/
public static final String FW_3_VER_ID = "/3/0/3";
// Update E // Update E
public static final String FW_UPDATE_ID = "/5/0/2"; public static final String FW_UPDATE_ID = "/5/0/2";
// Software // Software
public static final String SOFTWARE_UPDATE_COAP_RECOURSE = "softwareUpdateCoapRecourse";
public static final String SW_UPDATE = "Software update"; public static final String SW_UPDATE = "Software update";
public static final Integer SW_ID = 9; public static final Integer SW_ID = 9;
// Package W // Package W
public static final String SW_PACKAGE_ID = "/9/0/2"; public static final String SW_PACKAGE_ID = "/9/0/2";
// Package URI
public static final String SW_PACKAGE_URI_ID = "/9/0/3";
// Update State R // Update State R
public static final String SW_UPDATE_STATE_ID = "/9/0/7"; public static final String SW_UPDATE_STATE_ID = "/9/0/7";
// Update Result R // Update Result R
@ -232,7 +252,7 @@ public class LwM2mTransportUtil {
DELETE(11, "Delete"), DELETE(11, "Delete"),
// only for RPC // only for RPC
FW_UPDATE(12,"FirmwareUpdate"); FW_UPDATE(12, "FirmwareUpdate");
// FW_READ_INFO(12, "FirmwareReadInfo"), // FW_READ_INFO(12, "FirmwareReadInfo"),
// SW_READ_INFO(15, "SoftwareReadInfo"), // SW_READ_INFO(15, "SoftwareReadInfo"),
@ -355,20 +375,11 @@ public class LwM2mTransportUtil {
* FirmwareUpdateStatus { * FirmwareUpdateStatus {
* DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED * DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED
*/ */
public static OtaPackageUpdateStatus EqualsFwSateToFirmwareUpdateStatus(StateFw stateFw, UpdateResultFw updateResultFw) { public static OtaPackageUpdateStatus equalsFwSateFwResultToFirmwareUpdateStatus(StateFw stateFw, UpdateResultFw updateResultFw) {
switch (updateResultFw) { switch (updateResultFw) {
case INITIAL: case INITIAL:
switch (stateFw) { return equalsFwSateToFirmwareUpdateStatus(stateFw);
case IDLE: case UPDATE_SUCCESSFULLY:
return VERIFIED;
case DOWNLOADING:
return DOWNLOADING;
case DOWNLOADED:
return DOWNLOADED;
case UPDATING:
return UPDATING;
}
case UPDATE_SUCCESSFULLY:
return UPDATED; return UPDATED;
case NOT_ENOUGH: case NOT_ENOUGH:
case OUT_OFF_MEMORY: case OUT_OFF_MEMORY:
@ -384,6 +395,41 @@ public class LwM2mTransportUtil {
} }
} }
public static OtaPackageUpdateStatus equalsFwResultToFirmwareUpdateStatus(UpdateResultFw updateResultFw) {
switch (updateResultFw) {
case INITIAL:
return VERIFIED;
case UPDATE_SUCCESSFULLY:
return UPDATED;
case NOT_ENOUGH:
case OUT_OFF_MEMORY:
case CONNECTION_LOST:
case INTEGRITY_CHECK_FAILURE:
case UNSUPPORTED_TYPE:
case INVALID_URI:
case UPDATE_FAILED:
case UNSUPPORTED_PROTOCOL:
return FAILED;
default:
throw new CodecException("Invalid value stateFw %s for FirmwareUpdateStatus.", updateResultFw.name());
}
}
public static OtaPackageUpdateStatus equalsFwSateToFirmwareUpdateStatus(StateFw stateFw) {
switch (stateFw) {
case IDLE:
return VERIFIED;
case DOWNLOADING:
return DOWNLOADING;
case DOWNLOADED:
return DOWNLOADED;
case UPDATING:
return UPDATING;
default:
throw new CodecException("Invalid value stateFw %d for FirmwareUpdateStatus.", stateFw);
}
}
/** /**
* SW Update State R * SW Update State R
* 0: INITIAL Before downloading. (see 5.1.2.1) * 0: INITIAL Before downloading. (see 5.1.2.1)
@ -497,6 +543,101 @@ public class LwM2mTransportUtil {
} }
} }
public enum LwM2MClientStrategy {
CLIENT_STRATEGY_1(1, "Read only resources marked as observation"),
CLIENT_STRATEGY_2(2, "Read all client resources");
public int code;
public String type;
LwM2MClientStrategy(int code, String type) {
this.code = code;
this.type = type;
}
public static LwM2MClientStrategy fromStrategyClientByType(String type) {
for (LwM2MClientStrategy to : LwM2MClientStrategy.values()) {
if (to.type.equals(type)) {
return to;
}
}
throw new IllegalArgumentException(String.format("Unsupported Client Strategy type : %s", type));
}
public static LwM2MClientStrategy fromStrategyClientByCode(int code) {
for (LwM2MClientStrategy to : LwM2MClientStrategy.values()) {
if (to.code == code) {
return to;
}
}
throw new IllegalArgumentException(String.format("Unsupported Client Strategy code : %s", code));
}
}
public enum LwM2MFirmwareUpdateStrategy {
OBJ_5_BINARY(1, "ObjectId 5, Binary"),
OBJ_5_TEMP_URL(2, "ObjectId 5, URI"),
OBJ_19_BINARY(3, "ObjectId 19, Binary");
public int code;
public String type;
LwM2MFirmwareUpdateStrategy(int code, String type) {
this.code = code;
this.type = type;
}
public static LwM2MFirmwareUpdateStrategy fromStrategyFwByType(String type) {
for (LwM2MFirmwareUpdateStrategy to : LwM2MFirmwareUpdateStrategy.values()) {
if (to.type.equals(type)) {
return to;
}
}
throw new IllegalArgumentException(String.format("Unsupported FW State type : %s", type));
}
public static LwM2MFirmwareUpdateStrategy fromStrategyFwByCode(int code) {
for (LwM2MFirmwareUpdateStrategy to : LwM2MFirmwareUpdateStrategy.values()) {
if (to.code == code) {
return to;
}
}
throw new IllegalArgumentException(String.format("Unsupported FW Strategy code : %s", code));
}
}
public enum LwM2MSoftwareUpdateStrategy {
BINARY(1, "ObjectId 9, Binary"),
TEMP_URL(2, "ObjectId 9, URI");
public int code;
public String type;
LwM2MSoftwareUpdateStrategy(int code, String type) {
this.code = code;
this.type = type;
}
public static LwM2MSoftwareUpdateStrategy fromStrategySwByType(String type) {
for (LwM2MSoftwareUpdateStrategy to : LwM2MSoftwareUpdateStrategy.values()) {
if (to.type.equals(type)) {
return to;
}
}
throw new IllegalArgumentException(String.format("Unsupported SW Strategy type : %s", type));
}
public static LwM2MSoftwareUpdateStrategy fromStrategySwByCode(int code) {
for (LwM2MSoftwareUpdateStrategy to : LwM2MSoftwareUpdateStrategy.values()) {
if (to.code == code) {
return to;
}
}
throw new IllegalArgumentException(String.format("Unsupported SW Strategy code : %s", code));
}
}
/** /**
* FirmwareUpdateStatus { * FirmwareUpdateStatus {
* DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED * DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED
@ -534,10 +675,6 @@ public class LwM2mTransportUtil {
} }
} }
public static final String EVENT_AWAKE = "AWAKE";
public static final String RESPONSE_REQUEST_CHANNEL = "RESP_REQ";
public static final String RESPONSE_CHANNEL = "RESP";
public static final String OBSERVE_CHANNEL = "OBSERVE";
public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath
resourcePath) throws CodecException { resourcePath) throws CodecException {
@ -558,6 +695,36 @@ public class LwM2mTransportUtil {
} }
} }
public static LwM2mOtaConvert convertOtaUpdateValueToString (String pathIdVer, Object value, ResourceModel.Type currentType) {
String path = convertPathFromIdVerToObjectId(pathIdVer);
LwM2mOtaConvert lwM2mOtaConvert = new LwM2mOtaConvert();
if (path != null) {
if (FW_STATE_ID.equals(path)) {
lwM2mOtaConvert.setCurrentType(STRING);
lwM2mOtaConvert.setValue(StateFw.fromStateFwByCode(((Long) value).intValue()).type);
return lwM2mOtaConvert;
}
else if (FW_RESULT_ID.equals(path)) {
lwM2mOtaConvert.setCurrentType(STRING);
lwM2mOtaConvert.setValue(UpdateResultFw.fromUpdateResultFwByCode(((Long) value).intValue()).type);
return lwM2mOtaConvert;
}
else if (SW_UPDATE_STATE_ID.equals(path)) {
lwM2mOtaConvert.setCurrentType(STRING);
lwM2mOtaConvert.setValue(UpdateStateSw.fromUpdateStateSwByCode(((Long) value).intValue()).type);
return lwM2mOtaConvert;
}
else if (SW_RESULT_ID.equals(path)) {
lwM2mOtaConvert.setCurrentType(STRING);
lwM2mOtaConvert.setValue(UpdateResultSw.fromUpdateResultSwByCode(((Long) value).intValue()).type);
return lwM2mOtaConvert;
}
}
lwM2mOtaConvert.setCurrentType(currentType);
lwM2mOtaConvert.setValue(value);
return lwM2mOtaConvert;
}
public static LwM2mNode getLvM2mNodeToObject(LwM2mNode content) { public static LwM2mNode getLvM2mNodeToObject(LwM2mNode content) {
if (content instanceof LwM2mObject) { if (content instanceof LwM2mObject) {
return (LwM2mObject) content; return (LwM2mObject) content;
@ -633,11 +800,6 @@ public class LwM2mTransportUtil {
return null; return null;
} }
public static int getClientOnlyObserveAfterConnect(LwM2mClientProfile profile) {
return profile.getPostClientLwM2mSettings().getAsJsonObject().has("clientOnlyObserveAfterConnect") ?
profile.getPostClientLwM2mSettings().getAsJsonObject().get("clientOnlyObserveAfterConnect").getAsInt() : 1;
}
private static boolean getValidateCredentialsBodyFromThingsboard(JsonObject objectMsg) { private static boolean getValidateCredentialsBodyFromThingsboard(JsonObject objectMsg) {
return (objectMsg != null && return (objectMsg != null &&
!objectMsg.isJsonNull() && !objectMsg.isJsonNull() &&
@ -839,7 +1001,7 @@ public class LwM2mTransportUtil {
* Attribute pmax = new Attribute(MAXIMUM_PERIOD, "60"); * Attribute pmax = new Attribute(MAXIMUM_PERIOD, "60");
* Attribute [] attrs = {gt, st}; * Attribute [] attrs = {gt, st};
*/ */
public static DownlinkRequest createWriteAttributeRequest(String target, Object params, DefaultLwM2MTransportMsgHandler serviceImpl) { public static SimpleDownlinkRequest createWriteAttributeRequest(String target, Object params, DefaultLwM2MTransportMsgHandler serviceImpl) {
AttributeSet attrSet = new AttributeSet(createWriteAttributes(params, serviceImpl, target)); AttributeSet attrSet = new AttributeSet(createWriteAttributes(params, serviceImpl, target));
return attrSet.getAttributes().size() > 0 ? new WriteAttributesRequest(target, attrSet) : null; return attrSet.getAttributes().size() > 0 ? new WriteAttributesRequest(target, attrSet) : null;
} }
@ -914,7 +1076,7 @@ public class LwM2mTransportUtil {
case GREATER_THAN: case GREATER_THAN:
case LESSER_THAN: case LESSER_THAN:
case STEP: case STEP:
if (value.getClass().getSimpleName().equals("String") ) { if (value.getClass().getSimpleName().equals("String")) {
value = Double.valueOf((String) value); value = Double.valueOf((String) value);
} }
return serviceImpl.converter.convertValue(value, equalsResourceTypeGetSimpleName(value), FLOAT, new LwM2mPath(target)); return serviceImpl.converter.convertValue(value, equalsResourceTypeGetSimpleName(value), FLOAT, new LwM2mPath(target));
@ -928,11 +1090,11 @@ public class LwM2mTransportUtil {
return new Attribute(key, attrValue); return new Attribute(key, attrValue);
} catch (Exception e) { } catch (Exception e) {
log.error("CreateAttribute, not valid parameter key: [{}], attrValue: [{}], error: [{}]", key, attrValue, e.getMessage()); log.error("CreateAttribute, not valid parameter key: [{}], attrValue: [{}], error: [{}]", key, attrValue, e.getMessage());
return null; return null;
} }
} }
public static boolean isFwSwWords (String pathName) { public static boolean isFwSwWords(String pathName) {
return OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) return OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName)
|| OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE).equals(pathName) || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE).equals(pathName)
|| OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM).equals(pathName) || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM).equals(pathName)

22
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2MClientState.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.client;
public enum LwM2MClientState {
CREATED, REGISTERED, UNREGISTERED
}

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

@ -0,0 +1,31 @@
/**
* 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.Getter;
public class LwM2MClientStateException extends Exception {
private static final long serialVersionUID = 3307690997951364046L;
@Getter
private final LwM2MClientState state;
public LwM2MClientStateException(LwM2MClientState state, String message) {
super(message);
this.state = state;
}
}

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

@ -26,6 +26,7 @@ import org.eclipse.leshan.core.node.LwM2mObjectInstance;
import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mResource;
import org.eclipse.leshan.core.node.LwM2mSingleResource; import org.eclipse.leshan.core.node.LwM2mSingleResource;
import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.server.model.LwM2mModelProvider; import org.eclipse.leshan.server.model.LwM2mModelProvider;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
import org.eclipse.leshan.server.security.SecurityInfo; import org.eclipse.leshan.server.security.SecurityInfo;
@ -49,6 +50,8 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE;
@ -62,12 +65,28 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.g
@Slf4j @Slf4j
public class LwM2mClient implements Cloneable { public class LwM2mClient implements Cloneable {
private final String nodeId;
@Getter
private final String endpoint;
private final Lock lock;
@Getter
@Setter
private LwM2MClientState state;
@Getter
private final Map<String, ResourceValue> resources;
@Getter
private final Map<String, TsKvProto> delayedRequests;
@Getter
private final List<String> pendingReadRequests;
@Getter
private final Queue<LwM2mQueuedRequest> queuedRequests;
@Getter @Getter
private String deviceName; private String deviceName;
@Getter @Getter
private String deviceProfileName; private String deviceProfileName;
@Getter
private String endpoint;
@Getter @Getter
private String identity; private String identity;
@Getter @Getter
@ -75,8 +94,6 @@ public class LwM2mClient implements Cloneable {
@Getter @Getter
private UUID deviceId; private UUID deviceId;
@Getter @Getter
private UUID sessionId;
@Getter
private SessionInfoProto session; private SessionInfoProto session;
@Getter @Getter
private UUID profileId; private UUID profileId;
@ -92,15 +109,6 @@ public class LwM2mClient implements Cloneable {
private ValidateDeviceCredentialsResponse credentials; private ValidateDeviceCredentialsResponse credentials;
@Getter
private final Map<String, ResourceValue> resources;
@Getter
private final Map<String, TsKvProto> delayedRequests;
@Getter
@Setter
private final List<String> pendingReadRequests;
@Getter
private final Queue<LwM2mQueuedRequest> queuedRequests;
@Getter @Getter
private boolean init; private boolean init;
@ -108,20 +116,23 @@ public class LwM2mClient implements Cloneable {
return super.clone(); return super.clone();
} }
public LwM2mClient(String nodeId, String endpoint, String identity, SecurityInfo securityInfo, ValidateDeviceCredentialsResponse credentials, UUID profileId, UUID sessionId) { public LwM2mClient(String nodeId, String endpoint) {
this.nodeId = nodeId;
this.endpoint = endpoint; this.endpoint = endpoint;
this.identity = identity; this.lock = new ReentrantLock();
this.securityInfo = securityInfo;
this.credentials = credentials;
this.delayedRequests = new ConcurrentHashMap<>(); this.delayedRequests = new ConcurrentHashMap<>();
this.pendingReadRequests = new CopyOnWriteArrayList<>(); this.pendingReadRequests = new CopyOnWriteArrayList<>();
this.resources = new ConcurrentHashMap<>(); this.resources = new ConcurrentHashMap<>();
this.profileId = profileId;
this.init = false;
this.queuedRequests = new ConcurrentLinkedQueue<>(); this.queuedRequests = new ConcurrentLinkedQueue<>();
this.state = LwM2MClientState.CREATED;
}
this.fwUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.FIRMWARE); public void init(String identity, SecurityInfo securityInfo, ValidateDeviceCredentialsResponse credentials, UUID profileId, UUID sessionId) {
this.swUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.SOFTWARE); this.identity = identity;
this.securityInfo = securityInfo;
this.credentials = credentials;
this.profileId = profileId;
this.init = false;
if (this.credentials != null && this.credentials.hasDeviceInfo()) { if (this.credentials != null && this.credentials.hasDeviceInfo()) {
this.session = createSession(nodeId, sessionId, credentials); this.session = createSession(nodeId, sessionId, credentials);
this.deviceId = new UUID(session.getDeviceIdMSB(), session.getDeviceIdLSB()); this.deviceId = new UUID(session.getDeviceIdMSB(), session.getDeviceIdLSB());
@ -131,6 +142,14 @@ public class LwM2mClient implements Cloneable {
} }
} }
public void lock() {
lock.lock();
}
public void unlock() {
lock.unlock();
}
public void onDeviceUpdate(Device device, Optional<DeviceProfile> deviceProfileOpt) { public void onDeviceUpdate(Device device, Optional<DeviceProfile> deviceProfileOpt) {
SessionInfoProto.Builder builder = SessionInfoProto.newBuilder().mergeFrom(session); SessionInfoProto.Builder builder = SessionInfoProto.newBuilder().mergeFrom(session);
this.deviceId = device.getUuidId(); this.deviceId = device.getUuidId();
@ -193,9 +212,7 @@ public class LwM2mClient implements Cloneable {
public Object getResourceValue(String pathRezIdVer, String pathRezId) { public Object getResourceValue(String pathRezIdVer, String pathRezId) {
String pathRez = pathRezIdVer == null ? convertPathFromObjectIdToIdVer(pathRezId, this.registration) : pathRezIdVer; String pathRez = pathRezIdVer == null ? convertPathFromObjectIdToIdVer(pathRezId, this.registration) : pathRezIdVer;
if (this.resources.get(pathRez) != null) { if (this.resources.get(pathRez) != null) {
return this.resources.get(pathRez).getLwM2mResource().isMultiInstances() ? return this.resources.get(pathRez).getLwM2mResource().getValue();
this.resources.get(pathRez).getLwM2mResource().getValues() :
this.resources.get(pathRez).getLwM2mResource().getValue();
} }
return null; return null;
} }
@ -366,5 +383,31 @@ public class LwM2mClient implements Cloneable {
} }
} }
public ContentFormat getDefaultContentFormat() {
if (registration == null) {
return ContentFormat.DEFAULT;
} else if (registration.getLwM2mVersion().equals("1.0")) {
return ContentFormat.TLV;
} else {
return ContentFormat.TEXT;
}
}
public LwM2mFwSwUpdate getFwUpdate (LwM2mClientContext clientContext) {
if (this.fwUpdate == null) {
LwM2mClientProfile lwM2mClientProfile = clientContext.getProfile(this.getProfileId());
this.fwUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.FIRMWARE, lwM2mClientProfile.getFwUpdateStrategy());
}
return this.fwUpdate;
}
public LwM2mFwSwUpdate getSwUpdate (LwM2mClientContext clientContext) {
if (this.swUpdate == null) {
LwM2mClientProfile lwM2mClientProfile = clientContext.getProfile(this.getProfileId());
this.swUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.SOFTWARE, lwM2mClientProfile.getSwUpdateStrategy());
}
return this.fwUpdate;
}
} }

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

@ -27,21 +27,17 @@ import java.util.UUID;
public interface LwM2mClientContext { public interface LwM2mClientContext {
void removeClientByRegistrationId(String registrationId);
LwM2mClient getClientByEndpoint(String endpoint);
LwM2mClient getClientByRegistrationId(String registrationId); LwM2mClient getClientByRegistrationId(String registrationId);
LwM2mClient getClient(TransportProtos.SessionInfoProto sessionInfo); LwM2mClient getClientByEndpoint(String endpoint);
LwM2mClient getOrRegister(Registration registration); LwM2mClient getClientBySessionInfo(TransportProtos.SessionInfoProto sessionInfo);
LwM2mClient registerOrUpdate(Registration registration); void register(LwM2mClient lwM2MClient, Registration registration) throws LwM2MClientStateException;
LwM2mClient fetchClientByEndpoint(String endpoint); void updateRegistration(LwM2mClient client, Registration registration) throws LwM2MClientStateException;
Registration getRegistration(String registrationId); void unregister(LwM2mClient client, Registration registration) throws LwM2MClientStateException;
Collection<LwM2mClient> getLwM2mClients(); Collection<LwM2mClient> getLwM2mClients();
@ -55,9 +51,11 @@ public interface LwM2mClientContext {
LwM2mClientProfile profileUpdate(DeviceProfile deviceProfile); LwM2mClientProfile profileUpdate(DeviceProfile deviceProfile);
Set<String> getSupportedIdVerInClient(Registration registration); Set<String> getSupportedIdVerInClient(LwM2mClient registration);
LwM2mClient getClientByDeviceId(UUID deviceId); LwM2mClient getClientByDeviceId(UUID deviceId);
void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials); void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials);
} }

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

@ -19,16 +19,15 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
import org.eclipse.leshan.server.security.EditableSecurityStore;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.secure.EndpointSecurityInfo; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
import org.thingsboard.server.transport.lwm2m.server.store.TbEditableSecurityStore;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -37,6 +36,7 @@ import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import static org.eclipse.leshan.core.SecurityMode.NO_SEC; import static org.eclipse.leshan.core.SecurityMode.NO_SEC;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer;
@ -48,108 +48,122 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.c
public class LwM2mClientContextImpl implements LwM2mClientContext { public class LwM2mClientContextImpl implements LwM2mClientContext {
private final LwM2mTransportContext context; private final LwM2mTransportContext context;
private final TbEditableSecurityStore securityStore;
private final Map<String, LwM2mClient> lwM2mClientsByEndpoint = new ConcurrentHashMap<>(); private final Map<String, LwM2mClient> lwM2mClientsByEndpoint = new ConcurrentHashMap<>();
private final Map<String, LwM2mClient> lwM2mClientsByRegistrationId = new ConcurrentHashMap<>(); private final Map<String, LwM2mClient> lwM2mClientsByRegistrationId = new ConcurrentHashMap<>();
private Map<UUID, LwM2mClientProfile> profiles = new ConcurrentHashMap<>(); private Map<UUID, LwM2mClientProfile> profiles = new ConcurrentHashMap<>();
private final LwM2mCredentialsSecurityInfoValidator lwM2MCredentialsSecurityInfoValidator;
private final EditableSecurityStore securityStore;
@Override @Override
public LwM2mClient getClientByEndpoint(String endpoint) { public LwM2mClient getClientByEndpoint(String endpoint) {
return lwM2mClientsByEndpoint.get(endpoint); return lwM2mClientsByEndpoint.computeIfAbsent(endpoint, ep -> new LwM2mClient(context.getNodeId(), ep));
} }
@Override @Override
public LwM2mClient getClientByRegistrationId(String registrationId) { public void register(LwM2mClient lwM2MClient, Registration registration) throws LwM2MClientStateException {
return lwM2mClientsByRegistrationId.get(registrationId); lwM2MClient.lock();
try {
if (LwM2MClientState.UNREGISTERED.equals(lwM2MClient.getState())) {
throw new LwM2MClientStateException(lwM2MClient.getState(), "Client is in invalid state.");
}
TbLwM2MSecurityInfo securityInfo = securityStore.getTbLwM2MSecurityInfoByEndpoint(lwM2MClient.getEndpoint());
if (securityInfo.getSecurityMode() != null) {
if (securityInfo.getDeviceProfile() != null) {
UUID profileUuid = profileUpdate(securityInfo.getDeviceProfile()) != null ? securityInfo.getDeviceProfile().getUuidId() : null;
if (securityInfo.getSecurityInfo() != null) {
lwM2MClient.init(securityInfo.getSecurityInfo().getIdentity(), securityInfo.getSecurityInfo(), securityInfo.getMsg(), profileUuid, UUID.randomUUID());
} else if (NO_SEC.equals(securityInfo.getSecurityMode())) {
lwM2MClient.init(null, null, securityInfo.getMsg(), profileUuid, UUID.randomUUID());
} else {
throw new RuntimeException(String.format("Registration failed: device %s not found.", lwM2MClient.getEndpoint()));
}
} else {
throw new RuntimeException(String.format("Registration failed: device %s not found.", lwM2MClient.getEndpoint()));
}
} else {
throw new RuntimeException(String.format("Registration failed: FORBIDDEN, endpointId: %s", lwM2MClient.getEndpoint()));
}
lwM2MClient.setRegistration(registration);
this.lwM2mClientsByRegistrationId.put(registration.getId(), lwM2MClient);
lwM2MClient.setState(LwM2MClientState.REGISTERED);
} finally {
lwM2MClient.unlock();
}
} }
@Override @Override
public LwM2mClient getOrRegister(Registration registration) { public void updateRegistration(LwM2mClient lwM2MClient, Registration registration) throws LwM2MClientStateException {
if (registration == null) { lwM2MClient.lock();
return null; try {
if (!LwM2MClientState.REGISTERED.equals(lwM2MClient.getState())) {
throw new LwM2MClientStateException(lwM2MClient.getState(), "Client is in invalid state.");
}
lwM2MClient.setRegistration(registration);
} finally {
lwM2MClient.unlock();
} }
LwM2mClient client = lwM2mClientsByRegistrationId.get(registration.getId()); }
if (client == null) {
client = lwM2mClientsByEndpoint.get(registration.getEndpoint()); @Override
if (client == null) { public void unregister(LwM2mClient lwM2MClient, Registration registration) throws LwM2MClientStateException {
client = registerOrUpdate(registration); lwM2MClient.lock();
try {
if (!LwM2MClientState.REGISTERED.equals(lwM2MClient.getState())) {
throw new LwM2MClientStateException(lwM2MClient.getState(), "Client is in invalid state.");
}
lwM2mClientsByRegistrationId.remove(registration.getId());
Registration currentRegistration = lwM2MClient.getRegistration();
if (currentRegistration.getId().equals(registration.getId())) {
lwM2MClient.setState(LwM2MClientState.UNREGISTERED);
lwM2mClientsByEndpoint.remove(lwM2MClient.getEndpoint());
this.securityStore.remove(lwM2MClient.getEndpoint());
UUID profileId = lwM2MClient.getProfileId();
if (profileId != null) {
Optional<LwM2mClient> otherClients = lwM2mClientsByRegistrationId.values().stream().filter(e -> e.getProfileId().equals(profileId)).findFirst();
if (otherClients.isEmpty()) {
profiles.remove(profileId);
}
}
} else {
throw new LwM2MClientStateException(lwM2MClient.getState(), "Client has different registration.");
} }
} finally {
lwM2MClient.unlock();
} }
return client;
} }
@Override @Override
public LwM2mClient getClient(TransportProtos.SessionInfoProto sessionInfo) { public LwM2mClient getClientByRegistrationId(String registrationId) {
LwM2mClient lwM2mClient = lwM2mClientsByEndpoint.values().stream().filter(c -> return lwM2mClientsByRegistrationId.get(registrationId);
(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())) }
.equals((new UUID(c.getSession().getSessionIdMSB(), c.getSession().getSessionIdLSB())))
).findAny().get(); @Override
public LwM2mClient getClientBySessionInfo(TransportProtos.SessionInfoProto sessionInfo) {
LwM2mClient lwM2mClient = null;
Predicate<LwM2mClient> isClientFilter = c ->
(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()))
.equals((new UUID(c.getSession().getSessionIdMSB(), c.getSession().getSessionIdLSB())));
if (this.lwM2mClientsByEndpoint.size() > 0) {
lwM2mClient = this.lwM2mClientsByEndpoint.values().stream().filter(isClientFilter).findAny().orElse(null);
}
if (lwM2mClient == null && this.lwM2mClientsByRegistrationId.size() > 0) {
lwM2mClient = this.lwM2mClientsByRegistrationId.values().stream().filter(isClientFilter).findAny().orElse(null);
}
if (lwM2mClient == null) { if (lwM2mClient == null) {
log.warn("Device TimeOut? lwM2mClient is null."); log.warn("Device TimeOut? lwM2mClient is null.");
log.warn("SessionInfo input [{}], lwM2mClientsByEndpoint size: [{}]", sessionInfo, lwM2mClientsByEndpoint.values().size()); log.warn("SessionInfo input [{}], lwM2mClientsByEndpoint size: [{}] lwM2mClientsByRegistrationId: [{}]", sessionInfo, lwM2mClientsByEndpoint.values(), lwM2mClientsByRegistrationId.values());
log.error("", new RuntimeException()); log.error("", new RuntimeException());
} }
return lwM2mClient; return lwM2mClient;
} }
@Override
public LwM2mClient registerOrUpdate(Registration registration) {
LwM2mClient lwM2MClient = lwM2mClientsByEndpoint.get(registration.getEndpoint());
if (lwM2MClient == null) {
lwM2MClient = this.fetchClientByEndpoint(registration.getEndpoint());
}
lwM2MClient.setRegistration(registration);
// TODO: this remove is probably redundant. We should remove it.
// this.lwM2mClientsByEndpoint.remove(registration.getEndpoint());
this.lwM2mClientsByRegistrationId.put(registration.getId(), lwM2MClient);
return lwM2MClient;
}
public Registration getRegistration(String registrationId) { public Registration getRegistration(String registrationId) {
return this.lwM2mClientsByRegistrationId.get(registrationId).getRegistration(); return this.lwM2mClientsByRegistrationId.get(registrationId).getRegistration();
} }
@Override
public LwM2mClient fetchClientByEndpoint(String endpoint) {
EndpointSecurityInfo securityInfo = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfo(endpoint, LwM2mTransportUtil.LwM2mTypeServer.CLIENT);
if (securityInfo.getSecurityMode() != null) {
if (securityInfo.getDeviceProfile() != null) {
UUID profileUuid = profileUpdate(securityInfo.getDeviceProfile())!= null ?
securityInfo.getDeviceProfile().getUuidId() : null;
// TODO: for tests bug.
if (profileUuid== null) {
log.trace("input parameters toClientProfile if the result is null: [{}]", securityInfo.getDeviceProfile());
}
LwM2mClient client;
if (securityInfo.getSecurityInfo() != null) {
client = new LwM2mClient(context.getNodeId(), securityInfo.getSecurityInfo().getEndpoint(),
securityInfo.getSecurityInfo().getIdentity(), securityInfo.getSecurityInfo(),
securityInfo.getMsg(), profileUuid, UUID.randomUUID());
} else if (NO_SEC.equals(securityInfo.getSecurityMode())) {
client = new LwM2mClient(context.getNodeId(), endpoint,
null, null,
securityInfo.getMsg(), profileUuid, UUID.randomUUID());
} else {
throw new RuntimeException(String.format("Registration failed: device %s not found.", endpoint));
}
lwM2mClientsByEndpoint.put(client.getEndpoint(), client);
return client;
} else {
throw new RuntimeException(String.format("Registration failed: device %s not found.", endpoint));
}
} else {
throw new RuntimeException(String.format("Registration failed: FORBIDDEN, endpointId: %s", endpoint));
}
}
@Override @Override
public void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials) { public void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials) {
LwM2mClient client = new LwM2mClient(context.getNodeId(), registration.getEndpoint(), null, null, credentials, credentials.getDeviceProfile().getUuidId(), UUID.randomUUID()); LwM2mClient client = getClientByEndpoint(registration.getEndpoint());
lwM2mClientsByEndpoint.put(registration.getEndpoint(), client); client.init(null, null, credentials, credentials.getDeviceProfile().getUuidId(), UUID.randomUUID());
lwM2mClientsByRegistrationId.put(registration.getId(), client); lwM2mClientsByRegistrationId.put(registration.getId(), client);
profileUpdate(credentials.getDeviceProfile()); profileUpdate(credentials.getDeviceProfile());
} }
@ -171,7 +185,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
@Override @Override
public LwM2mClientProfile getProfile(Registration registration) { public LwM2mClientProfile getProfile(Registration registration) {
return this.getProfiles().get(getOrRegister(registration).getProfileId()); return this.getProfiles().get(getClientByEndpoint(registration.getEndpoint()).getProfileId());
} }
@Override @Override
@ -186,25 +200,18 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
if (lwM2MClientProfile != null) { if (lwM2MClientProfile != null) {
profiles.put(deviceProfile.getUuidId(), lwM2MClientProfile); profiles.put(deviceProfile.getUuidId(), lwM2MClientProfile);
return lwM2MClientProfile; return lwM2MClientProfile;
} } else {
else {
return null; return null;
} }
} }
/**
* if isVer - ok or default ver=DEFAULT_LWM2M_VERSION
*
* @param registration -
* @return - all objectIdVer in client
*/
@Override @Override
public Set<String> getSupportedIdVerInClient(Registration registration) { public Set<String> getSupportedIdVerInClient(LwM2mClient client) {
Set<String> clientObjects = ConcurrentHashMap.newKeySet(); Set<String> clientObjects = ConcurrentHashMap.newKeySet();
Arrays.stream(registration.getObjectLinks()).forEach(url -> { Arrays.stream(client.getRegistration().getObjectLinks()).forEach(link -> {
LwM2mPath pathIds = new LwM2mPath(url.getUrl()); LwM2mPath pathIds = new LwM2mPath(link.getUrl());
if (!pathIds.isRoot()) { if (!pathIds.isRoot()) {
clientObjects.add(convertPathFromObjectIdToIdVer(url.getUrl(), registration)); clientObjects.add(convertPathFromObjectIdToIdVer(link.getUrl(), client.getRegistration()));
} }
}); });
return (clientObjects.size() > 0) ? clientObjects : null; return (clientObjects.size() > 0) ? clientObjects : null;
@ -215,20 +222,4 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
return lwM2mClientsByRegistrationId.values().stream().filter(e -> deviceId.equals(e.getDeviceId())).findFirst().orElse(null); return lwM2mClientsByRegistrationId.values().stream().filter(e -> deviceId.equals(e.getDeviceId())).findFirst().orElse(null);
} }
@Override
public void removeClientByRegistrationId(String registrationId) {
LwM2mClient lwM2MClient = this.lwM2mClientsByRegistrationId.get(registrationId);
if (lwM2MClient != null) {
this.securityStore.remove(lwM2MClient.getEndpoint(), false);
this.lwM2mClientsByEndpoint.remove(lwM2MClient.getEndpoint());
this.lwM2mClientsByRegistrationId.remove(registrationId);
UUID profileId = lwM2MClient.getProfileId();
if (profileId != null) {
Optional<LwM2mClient> otherClients = lwM2mClientsByRegistrationId.values().stream().filter(e -> e.getProfileId().equals(profileId)).findFirst();
if (otherClients.isEmpty()) {
profiles.remove(profileId);
}
}
}
}
} }

30
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientProfile.java

@ -20,15 +20,23 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MClientStrategy;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MSoftwareUpdateStrategy;
@Data @Data
public class LwM2mClientProfile { public class LwM2mClientProfile {
private final String clientStrategyStr = "clientStrategy";
private final String fwUpdateStrategyStr = "fwUpdateStrategy";
private final String swUpdateStrategyStr = "swUpdateStrategy";
private TenantId tenantId; private TenantId tenantId;
/** /**
* {"clientLwM2mSettings": { * "clientLwM2mSettings": {
* clientUpdateValueAfterConnect: false; * "fwUpdateStrategy": "1",
* } * "swUpdateStrategy": "1",
* "clientStrategy": "1"
* }
**/ **/
private JsonObject postClientLwM2mSettings; private JsonObject postClientLwM2mSettings;
@ -85,5 +93,21 @@ public class LwM2mClientProfile {
} }
} }
public int getClientStrategy() {
return this.postClientLwM2mSettings.getAsJsonObject().has(this.clientStrategyStr) ?
Integer.parseInt(this.postClientLwM2mSettings.getAsJsonObject().get(this.clientStrategyStr).getAsString()) :
LwM2MClientStrategy.CLIENT_STRATEGY_1.code;
}
public int getFwUpdateStrategy() {
return this.postClientLwM2mSettings.getAsJsonObject().has(this.fwUpdateStrategyStr) ?
Integer.parseInt(this.postClientLwM2mSettings.getAsJsonObject().get(this.fwUpdateStrategyStr).getAsString()) :
LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code;
}
public int getSwUpdateStrategy() {
return this.postClientLwM2mSettings.getAsJsonObject().has(this.swUpdateStrategyStr) ?
Integer.parseInt(this.postClientLwM2mSettings.getAsJsonObject().get(this.swUpdateStrategyStr).getAsString()) :
LwM2MSoftwareUpdateStrategy.BINARY.code;
}
} }

8
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java → common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientRpcRequest.java

@ -57,7 +57,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.v
@Slf4j @Slf4j
@Data @Data
public class Lwm2mClientRpcRequest { public class LwM2mClientRpcRequest {
private Registration registration; private Registration registration;
private TransportProtos.SessionInfoProto sessionInfo; private TransportProtos.SessionInfoProto sessionInfo;
@ -75,10 +75,10 @@ public class Lwm2mClientRpcRequest {
private String infoMsg; private String infoMsg;
private String responseCode; private String responseCode;
public Lwm2mClientRpcRequest() { public LwM2mClientRpcRequest() {
} }
public Lwm2mClientRpcRequest(LwM2mTypeOper lwM2mTypeOper, String bodyParams, int requestId, public LwM2mClientRpcRequest(LwM2mTypeOper lwM2mTypeOper, String bodyParams, int requestId,
TransportProtos.SessionInfoProto sessionInfo, Registration registration, DefaultLwM2MTransportMsgHandler handler) { TransportProtos.SessionInfoProto sessionInfo, Registration registration, DefaultLwM2MTransportMsgHandler handler) {
this.registration = registration; this.registration = registration;
this.sessionInfo = sessionInfo; this.sessionInfo = sessionInfo;
@ -273,7 +273,7 @@ public class Lwm2mClientRpcRequest {
} }
private String getRezIdByResourceNameAndObjectInstanceId(String resourceName, DefaultLwM2MTransportMsgHandler handler) { private String getRezIdByResourceNameAndObjectInstanceId(String resourceName, DefaultLwM2MTransportMsgHandler handler) {
LwM2mClient lwM2mClient = handler.clientContext.getClient(this.sessionInfo); LwM2mClient lwM2mClient = handler.clientContext.getClientBySessionInfo(this.sessionInfo);
return lwM2mClient != null ? return lwM2mClient != null ?
lwM2mClient.getRezIdByResourceNameAndObjectInstanceId(resourceName, this.targetIdVer, handler.config.getModelProvider()) : lwM2mClient.getRezIdByResourceNameAndObjectInstanceId(resourceName, this.targetIdVer, handler.config.getModelProvider()) :
null; null;

210
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java

@ -20,6 +20,7 @@ import lombok.Setter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.eclipse.leshan.core.request.ContentFormat; import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.server.registration.Registration;
import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
@ -32,21 +33,32 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT;
import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE; import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE;
import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE;
import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.INITIATED;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATED;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING;
import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey; import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey;
import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RECOURSE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_3_VER_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_VER_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_NAME_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_NAME_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_19_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_5_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_URI_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_STATE_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_STATE_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_VER_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE;
@ -59,6 +71,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.S
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_UPDATE_STATE_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_UPDATE_STATE_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_VER_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_VER_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.equalsFwSateToFirmwareUpdateStatus;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.splitCamelCaseString; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.splitCamelCaseString;
@Slf4j @Slf4j
@ -106,23 +119,29 @@ public class LwM2mFwSwUpdate {
private final List<String> pendingInfoRequestsStart; private final List<String> pendingInfoRequestsStart;
@Getter @Getter
@Setter @Setter
private volatile Lwm2mClientRpcRequest rpcRequest; private volatile LwM2mClientRpcRequest rpcRequest;
@Getter
@Setter
private volatile int updateStrategy;
public LwM2mFwSwUpdate(LwM2mClient lwM2MClient, OtaPackageType type) { public LwM2mFwSwUpdate(LwM2mClient lwM2MClient, OtaPackageType type, int updateStrategy) {
this.lwM2MClient = lwM2MClient; this.lwM2MClient = lwM2MClient;
this.pendingInfoRequestsStart = new CopyOnWriteArrayList<>(); this.pendingInfoRequestsStart = new CopyOnWriteArrayList<>();
this.type = type; this.type = type;
this.stateUpdate = null; this.stateUpdate = null;
this.updateStrategy = updateStrategy;
this.initPathId(); this.initPathId();
} }
private void initPathId() { private void initPathId() {
if (FIRMWARE.equals(this.type)) { if (FIRMWARE.equals(this.type)) {
this.pathPackageId = FW_PACKAGE_ID; this.pathPackageId = LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy ?
FW_PACKAGE_5_ID : LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy ?
FW_PACKAGE_URI_ID : FW_PACKAGE_19_ID;
this.pathStateId = FW_STATE_ID; this.pathStateId = FW_STATE_ID;
this.pathResultId = FW_RESULT_ID; this.pathResultId = FW_RESULT_ID;
this.pathNameId = FW_NAME_ID; this.pathNameId = FW_NAME_ID;
this.pathVerId = FW_VER_ID; this.pathVerId = FW_5_VER_ID;
this.pathInstallId = FW_UPDATE_ID; this.pathInstallId = FW_UPDATE_ID;
this.wUpdate = FW_UPDATE; this.wUpdate = FW_UPDATE;
} else if (SOFTWARE.equals(this.type)) { } else if (SOFTWARE.equals(this.type)) {
@ -143,13 +162,13 @@ public class LwM2mFwSwUpdate {
} }
if (this.pendingInfoRequestsStart.size() == 0) { if (this.pendingInfoRequestsStart.size() == 0) {
this.infoFwSwUpdate = false; this.infoFwSwUpdate = false;
if (!OtaPackageUpdateStatus.DOWNLOADING.name().equals(this.stateUpdate)) { // if (!FAILED.name().equals(this.stateUpdate)) {
boolean conditionalStart = this.type.equals(FIRMWARE) ? this.conditionalFwUpdateStart() : boolean conditionalStart = this.type.equals(FIRMWARE) ? this.conditionalFwUpdateStart(handler) :
this.conditionalSwUpdateStart(); this.conditionalSwUpdateStart(handler);
if (conditionalStart) { if (conditionalStart) {
this.writeFwSwWare(handler, request); this.writeFwSwWare(handler, request);
}
} }
// }
} }
} }
@ -159,38 +178,47 @@ public class LwM2mFwSwUpdate {
*/ */
public void writeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) { public void writeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) {
if (this.currentId != null) { if (this.currentId != null) {
this.stateUpdate = OtaPackageUpdateStatus.DOWNLOADING.name(); this.stateUpdate = OtaPackageUpdateStatus.INITIATED.name();
this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
int chunkSize = 0;
int chunk = 0;
byte[] firmwareChunk = handler.otaPackageDataCache.get(this.currentId.toString(), chunkSize, chunk);
String targetIdVer = convertPathFromObjectIdToIdVer(this.pathPackageId, this.lwM2MClient.getRegistration()); String targetIdVer = convertPathFromObjectIdToIdVer(this.pathPackageId, this.lwM2MClient.getRegistration());
String fwMsg = String.format("%s: Start type operation %s paths: %s", LOG_LW2M_INFO, String fwMsg = String.format("%s: Start type operation %s paths: %s", LOG_LW2M_INFO,
LwM2mTransportUtil.LwM2mTypeOper.FW_UPDATE.name(), FW_PACKAGE_ID); LwM2mTransportUtil.LwM2mTypeOper.FW_UPDATE.name(), this.pathPackageId);
handler.sendLogsToThingsboard(fwMsg, lwM2MClient.getRegistration().getId()); handler.sendLogsToThingsboard(fwMsg, lwM2MClient.getRegistration().getId());
log.warn("8) Start firmware Update. Send save to: [{}] ver: [{}] path: [{}]", this.lwM2MClient.getDeviceName(), this.currentVersion, targetIdVer); log.warn("8) Start firmware Update. Send save to: [{}] ver: [{}] path: [{}]", this.lwM2MClient.getDeviceName(), this.currentVersion, targetIdVer);
request.sendAllRequest(this.lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE.getName(), if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy) {
firmwareChunk, handler.config.getTimeout(), this.rpcRequest); int chunkSize = 0;
} int chunk = 0;
else { byte[] firmwareChunk = handler.otaPackageDataCache.get(this.currentId.toString(), chunkSize, chunk);
request.sendAllRequest(this.lwM2MClient, targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE,
firmwareChunk, handler.config.getTimeout(), this.rpcRequest);
} else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy) {
String apiFont = "coap://176.36.143.9:5685";
String uri = apiFont + "/" + FIRMWARE_UPDATE_COAP_RECOURSE + "/" + this.currentId.toString();
log.warn("89) coapUri: [{}]", uri);
request.sendAllRequest(this.lwM2MClient, targetIdVer, WRITE_REPLACE, null,
uri, handler.config.getTimeout(), this.rpcRequest);
} else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy) {
}
} else {
String msgError = "FirmWareId is null."; String msgError = "FirmWareId is null.";
log.warn("6) [{}]", msgError); log.warn("6) [{}]", msgError);
if (this.rpcRequest != null) { if (this.rpcRequest != null) {
handler.sentRpcResponse(this.rpcRequest, CONTENT.name(), msgError, LOG_LW2M_ERROR); handler.sentRpcResponse(this.rpcRequest, CONTENT.name(), msgError, LOG_LW2M_ERROR);
} }
log.error (msgError); log.error(msgError);
this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError);
} }
} }
public void sendLogs(DefaultLwM2MTransportMsgHandler handler, String typeOper, String typeInfo, String msgError) { public void sendLogs(DefaultLwM2MTransportMsgHandler handler, String typeOper, String typeInfo, String msgError) {
this.sendSateOnThingsBoard(handler); // this.sendSateOnThingsBoard(handler);
String msg = String.format("%s: %s, %s, pkgVer: %s: pkgName - %s state - %s.", String msg = String.format("%s: %s, %s, pkgVer: %s: pkgName - %s state - %s.",
typeInfo, this.wUpdate, typeOper, this.currentVersion, this.currentTitle, this.stateUpdate); typeInfo, this.wUpdate, typeOper, this.currentVersion, this.currentTitle, this.stateUpdate);
if (LOG_LW2M_ERROR.equals(typeInfo)) { if (LOG_LW2M_ERROR.equals(typeInfo)) {
msg = String.format("%s Error: %s", msg, msgError); msg = String.format("%s Error: %s", msg, msgError);
} }
handler.sendLogsToThingsboard(msg, lwM2MClient.getRegistration().getId()); handler.sendLogsToThingsboard(lwM2MClient, msg);
} }
@ -200,32 +228,49 @@ public class LwM2mFwSwUpdate {
* send execute * send execute
*/ */
public void executeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) { public void executeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) {
this.setStateUpdate(UPDATING.name());
this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null); this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null);
request.sendAllRequest(this.lwM2MClient.getRegistration(), this.pathInstallId, EXECUTE, ContentFormat.TLV.getName(), request.sendAllRequest(this.lwM2MClient, this.pathInstallId, EXECUTE, null, 0, this.rpcRequest);
null, 0, this.rpcRequest);
} }
/** /**
* Firmware start: * Firmware start: Check if the version has changed and launch a new update.
* -ObjectId 5, Binary or ObjectId 5, URI
* -- If the result of the update - errors (more than 1) - This means that the previous. the update failed. * -- If the result of the update - errors (more than 1) - This means that the previous. the update failed.
* - We launch the update regardless of the state of the firmware and its version. * - We launch the update regardless of the state of the firmware and its version.
* -- If the result of the update is not errors (equal to 1 or 0) and ver is not empty - This means that before the update has passed. * -- If the result of the update - errors (more than 1) - This means that the previous. the update failed.
* -- If the result of the update is not errors and is empty - This means that there has not been an update yet. * * ObjectId 5, Binary
* - Check if the version has changed and launch a new update. * -- If the result of the update is not errors (equal to 1 or 0) and ver in Object 5 is not empty - it means that the previous update has passed.
* Compare current versions by equals.
* * ObjectId 5, URI
* -- If the result of the update is not errors (equal to 1 or 0) and ver in Object 5 is not empty - it means that the previous update has passed.
* Compare current versions by contains.
*/ */
private boolean conditionalFwUpdateStart() { private boolean conditionalFwUpdateStart(DefaultLwM2MTransportMsgHandler handler) {
Long updateResultFw = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); Long updateResultFw = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
String ver5 = (String) this.lwM2MClient.getResourceValue(null, this.pathVerId);
String pathName = (String) this.lwM2MClient.getResourceValue(null, this.pathNameId);
String ver3 = (String) this.lwM2MClient.getResourceValue(null, FW_3_VER_ID);
// #1/#2 // #1/#2
return updateResultFw > LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code || String fwMsg = null;
( if ((this.currentVersion != null && (
(updateResultFw <= LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code ver5 != null && ver5.equals(this.currentVersion) ||
) && ver3 != null && ver3.contains(this.currentVersion)
( )) ||
(this.currentVersion != null && !this.currentVersion.equals(this.lwM2MClient.getResourceValue(null, this.pathVerId))) || (this.currentTitle != null && pathName != null && this.currentTitle.equals(pathName))) {
(this.currentTitle != null && !this.currentTitle.equals(this.lwM2MClient.getResourceValue(null, this.pathNameId))) fwMsg = String.format("%s: The update was interrupted. The device has the same version: %s.", LOG_LW2M_ERROR,
) this.currentVersion);
); }
else if (updateResultFw != null && updateResultFw > LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) {
fwMsg = String.format("%s: The update was interrupted. The device has the status UpdateResult: error (%d).", LOG_LW2M_ERROR,
updateResultFw);
}
if (fwMsg != null) {
handler.sendLogsToThingsboard(fwMsg, lwM2MClient.getRegistration().getId());
return false;
}
else {
return true;
}
} }
@ -249,7 +294,7 @@ public class LwM2mFwSwUpdate {
/** /**
* After operation Execute success inspection Update Result : * After operation Execute success inspection Update Result :
* > 1 error: "Firmware updated successfully" * > 1 error: "Firmware updated successfully"
*/ */
public boolean conditionalFwExecuteAfterError() { public boolean conditionalFwExecuteAfterError() {
Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
@ -265,7 +310,7 @@ public class LwM2mFwSwUpdate {
* - If Update Result is not errors and ver is not empty - This means that before unInstall update * - If Update Result is not errors and ver is not empty - This means that before unInstall update
* * - Check if the version has changed and launch a new update. * * - Check if the version has changed and launch a new update.
*/ */
private boolean conditionalSwUpdateStart() { private boolean conditionalSwUpdateStart(DefaultLwM2MTransportMsgHandler handler) {
Long updateResultSw = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); Long updateResultSw = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
// #1/#2 // #1/#2
return updateResultSw >= LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code || return updateResultSw >= LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code ||
@ -293,7 +338,7 @@ public class LwM2mFwSwUpdate {
* -- inspection Update Result: * -- inspection Update Result:
* ---- FW если Update Result == 1 ("Firmware updated successfully") или SW если Update Result == 2 ("Software successfully installed.") * ---- FW если Update Result == 1 ("Firmware updated successfully") или SW если Update Result == 2 ("Software successfully installed.")
* -- fw_state/sw_state = UPDATED * -- fw_state/sw_state = UPDATED
* * <p>
* After finish operation Execute (error): * After finish operation Execute (error):
* -- inspection updateResult and send to thingsboard info about error * -- inspection updateResult and send to thingsboard info about error
* --- send to telemetry ( key - this is name Update Result in model) ( * --- send to telemetry ( key - this is name Update Result in model) (
@ -326,7 +371,7 @@ public class LwM2mFwSwUpdate {
/** /**
* After operation Execute success inspection Update Result : * After operation Execute success inspection Update Result :
* >= 50 - error "NOT_ENOUGH_STORAGE" * >= 50 - error "NOT_ENOUGH_STORAGE"
*/ */
public boolean conditionalSwExecuteAfterError() { public boolean conditionalSwExecuteAfterError() {
Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
@ -334,10 +379,10 @@ public class LwM2mFwSwUpdate {
} }
private void observeStateUpdate(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) { private void observeStateUpdate(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) {
request.sendAllRequest(lwM2MClient.getRegistration(), request.sendAllRequest(lwM2MClient,
convertPathFromObjectIdToIdVer(this.pathStateId, this.lwM2MClient.getRegistration()), OBSERVE, convertPathFromObjectIdToIdVer(this.pathStateId, this.lwM2MClient.getRegistration()), OBSERVE,
null, null, 0, null); null, null, 0, null);
request.sendAllRequest(lwM2MClient.getRegistration(), request.sendAllRequest(lwM2MClient,
convertPathFromObjectIdToIdVer(this.pathResultId, this.lwM2MClient.getRegistration()), OBSERVE, convertPathFromObjectIdToIdVer(this.pathResultId, this.lwM2MClient.getRegistration()), OBSERVE,
null, null, 0, null); null, null, 0, null);
} }
@ -355,18 +400,75 @@ public class LwM2mFwSwUpdate {
public void sendReadObserveInfo(LwM2mTransportRequest request) { public void sendReadObserveInfo(LwM2mTransportRequest request) {
this.infoFwSwUpdate = true; this.infoFwSwUpdate = true;
this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer(
this.pathVerId, this.lwM2MClient.getRegistration()));
this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer(
this.pathNameId, this.lwM2MClient.getRegistration()));
this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer(
this.pathStateId, this.lwM2MClient.getRegistration())); this.pathStateId, this.lwM2MClient.getRegistration()));
this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer(
this.pathResultId, this.lwM2MClient.getRegistration())); this.pathResultId, this.lwM2MClient.getRegistration()));
this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer(
FW_3_VER_ID, this.lwM2MClient.getRegistration()));
if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy ||
LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy ||
SOFTWARE.equals(this.type)) {
this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer(
this.pathVerId, this.lwM2MClient.getRegistration()));
this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer(
this.pathNameId, this.lwM2MClient.getRegistration()));
}
this.pendingInfoRequestsStart.forEach(pathIdVer -> { this.pendingInfoRequestsStart.forEach(pathIdVer -> {
request.sendAllRequest(this.lwM2MClient.getRegistration(), pathIdVer, OBSERVE, ContentFormat.TLV.getName(), request.sendAllRequest(this.lwM2MClient, pathIdVer, OBSERVE, null, 0, this.rpcRequest);
null, 0, this.rpcRequest);
}); });
} }
/**
* Before operation Execute (FwUpdate) inspection Update Result :
* - after finished operation Write result: success (FwUpdate): fw_state = DOWNLOADED
* - before start operation Execute (FwUpdate) Update Result = 0 - Initial value
* - start Execute (FwUpdate)
* After finished operation Execute (FwUpdate) inspection Update Result :
* - after start operation Execute (FwUpdate): fw_state = UPDATING
* - after success finished operation Execute (FwUpdate) Update Result == 1 ("Firmware updated successfully")
* - finished operation Execute (FwUpdate)
*/
public void updateStateOta(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request,
Registration registration, String path, int value) {
if (OBJ_5_BINARY.code == this.getUpdateStrategy()) {
if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) {
if (DOWNLOADED.name().equals(this.getStateUpdate())
&& this.conditionalFwExecuteStart()) {
this.executeFwSwWare(handler, request);
} else if (UPDATING.name().equals(this.getStateUpdate())
&& this.conditionalFwExecuteAfterSuccess()) {
this.finishFwSwUpdate(handler, true);
} else if (UPDATING.name().equals(this.getStateUpdate())
&& this.conditionalFwExecuteAfterError()) {
this.finishFwSwUpdate(handler, false);
}
}
} else if (OBJ_5_TEMP_URL.code == this.getUpdateStrategy()) {
if (this.currentId != null && (convertPathFromObjectIdToIdVer(FW_STATE_ID, registration).equals(path))) {
String state = equalsFwSateToFirmwareUpdateStatus(LwM2mTransportUtil.StateFw.fromStateFwByCode(value)).name();
if (StringUtils.isNotEmpty(state) && !FAILED.name().equals(this.stateUpdate) && !state.equals(this.stateUpdate)) {
this.stateUpdate = state;
this.sendSateOnThingsBoard(handler);
}
if (value == LwM2mTransportUtil.StateFw.DOWNLOADED.code) {
this.executeFwSwWare(handler, request);
}
handler.firmwareUpdateState.put(lwM2MClient.getEndpoint(), value);
}
if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) {
if (this.currentId != null && value == LwM2mTransportUtil.UpdateResultFw.INITIAL.code) {
this.setStateUpdate(INITIATED.name());
} else if (this.currentId != null && value == LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) {
this.setStateUpdate(UPDATED.name());
} else if (value > LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) {
this.setStateUpdate(FAILED.name());
}
this.sendSateOnThingsBoard(handler);
}
} else if (OBJ_19_BINARY.code == this.getUpdateStrategy()) {
}
}
} }

27
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbEditableSecurityStore.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.store;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
public interface TbEditableSecurityStore extends TbSecurityStore {
void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException;
void remove(String endpoint);
}

130
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbInMemorySecurityStore.java

@ -0,0 +1,130 @@
/**
* 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.store;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.eclipse.leshan.server.security.SecurityInfo;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class TbInMemorySecurityStore implements TbEditableSecurityStore {
// lock for the two maps
protected final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
protected final Lock readLock = readWriteLock.readLock();
protected final Lock writeLock = readWriteLock.writeLock();
// by client end-point
protected Map<String, TbLwM2MSecurityInfo> securityByEp = new HashMap<>();
// by PSK identity
protected Map<String, TbLwM2MSecurityInfo> securityByIdentity = new HashMap<>();
public TbInMemorySecurityStore() {
}
/**
* {@inheritDoc}
*/
@Override
public SecurityInfo getByEndpoint(String endpoint) {
readLock.lock();
try {
TbLwM2MSecurityInfo securityInfo = securityByEp.get(endpoint);
if (securityInfo != null) {
return securityInfo.getSecurityInfo();
} else {
return null;
}
} finally {
readLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public SecurityInfo getByIdentity(String identity) {
readLock.lock();
try {
TbLwM2MSecurityInfo securityInfo = securityByIdentity.get(identity);
if (securityInfo != null) {
return securityInfo.getSecurityInfo();
} else {
return null;
}
} finally {
readLock.unlock();
}
}
@Override
public void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException {
writeLock.lock();
try {
String identity = null;
if (tbSecurityInfo.getSecurityInfo() != null) {
identity = tbSecurityInfo.getSecurityInfo().getIdentity();
if (identity != null) {
TbLwM2MSecurityInfo infoByIdentity = securityByIdentity.get(identity);
if (infoByIdentity != null && !tbSecurityInfo.getSecurityInfo().getEndpoint().equals(infoByIdentity.getEndpoint())) {
throw new NonUniqueSecurityInfoException("PSK Identity " + identity + " is already used");
}
securityByIdentity.put(tbSecurityInfo.getSecurityInfo().getIdentity(), tbSecurityInfo);
}
}
TbLwM2MSecurityInfo previous = securityByEp.put(tbSecurityInfo.getEndpoint(), tbSecurityInfo);
if (previous != null && previous.getSecurityInfo() != null) {
String previousIdentity = previous.getSecurityInfo().getIdentity();
if (previousIdentity != null && !previousIdentity.equals(identity)) {
securityByIdentity.remove(previousIdentity);
}
}
} finally {
writeLock.unlock();
}
}
@Override
public void remove(String endpoint) {
writeLock.lock();
try {
TbLwM2MSecurityInfo securityInfo = securityByEp.remove(endpoint);
if (securityInfo != null && securityInfo.getSecurityInfo() != null && securityInfo.getSecurityInfo().getIdentity() != null) {
securityByIdentity.remove(securityInfo.getSecurityInfo().getIdentity());
}
} finally {
writeLock.unlock();
}
}
@Override
public TbLwM2MSecurityInfo getTbLwM2MSecurityInfoByEndpoint(String endpoint) {
readLock.lock();
try {
return securityByEp.get(endpoint);
} finally {
readLock.unlock();
}
}
}

54
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisRegistrationStore.java

@ -22,11 +22,13 @@ import org.eclipse.leshan.core.Destroyable;
import org.eclipse.leshan.core.Startable; import org.eclipse.leshan.core.Startable;
import org.eclipse.leshan.core.Stoppable; import org.eclipse.leshan.core.Stoppable;
import org.eclipse.leshan.core.observation.Observation; import org.eclipse.leshan.core.observation.Observation;
import org.eclipse.leshan.core.request.Identity;
import org.eclipse.leshan.core.util.NamedThreadFactory; import org.eclipse.leshan.core.util.NamedThreadFactory;
import org.eclipse.leshan.core.util.Validate; import org.eclipse.leshan.core.util.Validate;
import org.eclipse.leshan.server.californium.observation.ObserveUtil; import org.eclipse.leshan.server.californium.observation.ObserveUtil;
import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore; import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore;
import org.eclipse.leshan.server.redis.RedisRegistrationStore; import org.eclipse.leshan.server.redis.RedisRegistrationStore;
import org.eclipse.leshan.server.redis.serialization.IdentitySerDes;
import org.eclipse.leshan.server.redis.serialization.ObservationSerDes; import org.eclipse.leshan.server.redis.serialization.ObservationSerDes;
import org.eclipse.leshan.server.redis.serialization.RegistrationSerDes; import org.eclipse.leshan.server.redis.serialization.RegistrationSerDes;
import org.eclipse.leshan.server.registration.Deregistration; import org.eclipse.leshan.server.registration.Deregistration;
@ -73,6 +75,7 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
private static final String REG_EP = "REG:EP:"; // (Endpoint => Registration) private static final String REG_EP = "REG:EP:"; // (Endpoint => Registration)
private static final String REG_EP_REGID_IDX = "EP:REGID:"; // secondary index key (Registration ID => Endpoint) private static final String REG_EP_REGID_IDX = "EP:REGID:"; // secondary index key (Registration ID => Endpoint)
private static final String REG_EP_ADDR_IDX = "EP:ADDR:"; // secondary index key (Socket Address => Endpoint) private static final String REG_EP_ADDR_IDX = "EP:ADDR:"; // secondary index key (Socket Address => Endpoint)
private static final String REG_EP_IDENTITY = "EP:IDENTITY:"; // secondary index key (Identity => Endpoint)
private static final String LOCK_EP = "LOCK:EP:"; private static final String LOCK_EP = "LOCK:EP:";
private static final byte[] OBS_TKN = "OBS:TKN:".getBytes(UTF_8); private static final byte[] OBS_TKN = "OBS:TKN:".getBytes(UTF_8);
private static final String OBS_TKNS_REGID_IDX = "TKNS:REGID:"; // secondary index (token list by registration) private static final String OBS_TKNS_REGID_IDX = "TKNS:REGID:"; // secondary index (token list by registration)
@ -155,6 +158,8 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
connection.set(regid_idx, registration.getEndpoint().getBytes(UTF_8)); connection.set(regid_idx, registration.getEndpoint().getBytes(UTF_8));
byte[] addr_idx = toRegAddrKey(registration.getSocketAddress()); byte[] addr_idx = toRegAddrKey(registration.getSocketAddress());
connection.set(addr_idx, registration.getEndpoint().getBytes(UTF_8)); connection.set(addr_idx, registration.getEndpoint().getBytes(UTF_8));
byte[] identity_idx = toRegIdentityKey(registration.getIdentity());
connection.set(identity_idx, registration.getEndpoint().getBytes(UTF_8));
// Add or update expiration // Add or update expiration
addOrUpdateExpiration(connection, registration); addOrUpdateExpiration(connection, registration);
@ -167,6 +172,9 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
if (!oldRegistration.getSocketAddress().equals(registration.getSocketAddress())) { if (!oldRegistration.getSocketAddress().equals(registration.getSocketAddress())) {
removeAddrIndex(connection, oldRegistration); removeAddrIndex(connection, oldRegistration);
} }
if (!oldRegistration.getIdentity().equals(registration.getIdentity())) {
removeIdentityIndex(connection, oldRegistration);
}
// remove old observation // remove old observation
Collection<Observation> obsRemoved = unsafeRemoveAllObservations(connection, oldRegistration.getId()); Collection<Observation> obsRemoved = unsafeRemoveAllObservations(connection, oldRegistration.getId());
@ -222,6 +230,9 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
if (!r.getSocketAddress().equals(updatedRegistration.getSocketAddress())) { if (!r.getSocketAddress().equals(updatedRegistration.getSocketAddress())) {
removeAddrIndex(connection, r); removeAddrIndex(connection, r);
} }
if (!r.getIdentity().equals(updatedRegistration.getIdentity())) {
removeIdentityIndex(connection, r);
}
return new UpdatedRegistration(r, updatedRegistration); return new UpdatedRegistration(r, updatedRegistration);
@ -268,6 +279,22 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
} }
} }
@Override
public Registration getRegistrationByIdentity(Identity identity) {
Validate.notNull(identity);
try (var connection = connectionFactory.getConnection()) {
byte[] ep = connection.get(toRegIdentityKey(identity));
if (ep == null) {
return null;
}
byte[] data = connection.get(toEndpointKey(ep));
if (data == null) {
return null;
}
return deserializeReg(data);
}
}
@Override @Override
public Iterator<Registration> getAllRegistrations() { public Iterator<Registration> getAllRegistrations() {
try (var connection = connectionFactory.getConnection()) { try (var connection = connectionFactory.getConnection()) {
@ -325,6 +352,7 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
connection.del(toEndpointKey(r.getEndpoint())); connection.del(toEndpointKey(r.getEndpoint()));
Collection<Observation> obsRemoved = unsafeRemoveAllObservations(connection, r.getId()); Collection<Observation> obsRemoved = unsafeRemoveAllObservations(connection, r.getId());
removeAddrIndex(connection, r); removeAddrIndex(connection, r);
removeIdentityIndex(connection, r);
removeExpiration(connection, r); removeExpiration(connection, r);
return new Deregistration(r, obsRemoved); return new Deregistration(r, obsRemoved);
} }
@ -337,20 +365,27 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
} }
} }
private void removeAddrIndex(RedisConnection connection, Registration r) {
removeSecondaryIndex(connection, toRegAddrKey(r.getSocketAddress()), r.getEndpoint());
}
private void removeIdentityIndex(RedisConnection connection, Registration r) {
removeSecondaryIndex(connection, toRegIdentityKey(r.getIdentity()), r.getEndpoint());
}
//TODO: JedisCluster didn't implement Transaction, maybe should use some advanced key creation strategies //TODO: JedisCluster didn't implement Transaction, maybe should use some advanced key creation strategies
private void removeAddrIndex(RedisConnection connection, Registration registration) { private void removeSecondaryIndex(RedisConnection connection, byte[] indexKey, String endpointName) {
// Watch the key to remove. // Watch the key to remove.
byte[] regAddrKey = toRegAddrKey(registration.getSocketAddress()); // connection.watch(indexKey);
// connection.watch(regAddrKey);
byte[] epFromAddr = connection.get(regAddrKey); byte[] epFromAddr = connection.get(indexKey);
// Delete the key if needed. // Delete the key if needed.
if (Arrays.equals(epFromAddr, registration.getEndpoint().getBytes(UTF_8))) { if (Arrays.equals(epFromAddr, endpointName.getBytes(UTF_8))) {
// Try to delete the key // Try to delete the key
// connection.multi(); // connection.multi();
connection.del(regAddrKey); connection.del(indexKey);
// connection.exec(); // connection.exec();
// if transaction failed this is not an issue as the socket address is probably reused and we don't neeed to // if transaction failed this is not an issue as the index is probably reused and we don't need to
// delete it anymore. // delete it anymore.
} else { } else {
// the key must not be deleted. // the key must not be deleted.
@ -374,6 +409,10 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
return toKey(REG_EP_ADDR_IDX, addr.getAddress().toString() + ":" + addr.getPort()); return toKey(REG_EP_ADDR_IDX, addr.getAddress().toString() + ":" + addr.getPort());
} }
private byte[] toRegIdentityKey(Identity identity) {
return toKey(REG_EP_IDENTITY, IdentitySerDes.serialize(identity).toString());
}
private byte[] toEndpointKey(String endpoint) { private byte[] toEndpointKey(String endpoint) {
return toKey(REG_EP, endpoint); return toKey(REG_EP, endpoint);
} }
@ -723,7 +762,6 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
@Override @Override
public void run() { public void run() {
try (var connection = connectionFactory.getConnection()) { try (var connection = connectionFactory.getConnection()) {
Set<byte[]> endpointsExpired = connection.zRangeByScore(EXP_EP, Double.NEGATIVE_INFINITY, Set<byte[]> endpointsExpired = connection.zRangeByScore(EXP_EP, Double.NEGATIVE_INFINITY,
System.currentTimeMillis(), 0, cleanLimit); System.currentTimeMillis(), 0, cleanLimit);

141
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisSecurityStore.java

@ -24,13 +24,14 @@ import org.springframework.data.redis.connection.RedisClusterConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions; import org.springframework.data.redis.core.ScanOptions;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
public class TbLwM2mRedisSecurityStore implements EditableSecurityStore { public class TbLwM2mRedisSecurityStore implements TbEditableSecurityStore {
private static final String SEC_EP = "SEC#EP#"; private static final String SEC_EP = "SEC#EP#";
private static final String PSKID_SEC = "PSKID#SEC"; private static final String PSKID_SEC = "PSKID#SEC";
@ -72,73 +73,89 @@ public class TbLwM2mRedisSecurityStore implements EditableSecurityStore {
} }
@Override @Override
public Collection<SecurityInfo> getAll() { public void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException {
try (var connection = connectionFactory.getConnection()) { //TODO: implement
Collection<SecurityInfo> list = new LinkedList<>();
ScanOptions scanOptions = ScanOptions.scanOptions().count(100).match(SEC_EP + "*").build();
List<Cursor<byte[]>> scans = new ArrayList<>();
if (connection instanceof RedisClusterConnection) {
((RedisClusterConnection) connection).clusterGetNodes().forEach(node -> {
scans.add(((RedisClusterConnection) connection).scan(node, scanOptions));
});
} else {
scans.add(connection.scan(scanOptions));
}
scans.forEach(scan -> {
scan.forEachRemaining(key -> {
byte[] element = connection.get(key);
list.add(deserialize(element));
});
});
return list;
}
} }
@Override @Override
public SecurityInfo add(SecurityInfo info) throws NonUniqueSecurityInfoException { public TbLwM2MSecurityInfo getTbLwM2MSecurityInfoByEndpoint(String endpoint) {
byte[] data = serialize(info); //TODO: implement
try (var connection = connectionFactory.getConnection()) { return null;
if (info.getIdentity() != null) {
// populate the secondary index (security info by PSK id)
String oldEndpoint = new String(connection.hGet(PSKID_SEC.getBytes(), info.getIdentity().getBytes()));
if (!oldEndpoint.equals(info.getEndpoint())) {
throw new NonUniqueSecurityInfoException("PSK Identity " + info.getIdentity() + " is already used");
}
connection.hSet(PSKID_SEC.getBytes(), info.getIdentity().getBytes(), info.getEndpoint().getBytes());
}
byte[] previousData = connection.getSet((SEC_EP + info.getEndpoint()).getBytes(), data);
SecurityInfo previous = previousData == null ? null : deserialize(previousData);
String previousIdentity = previous == null ? null : previous.getIdentity();
if (previousIdentity != null && !previousIdentity.equals(info.getIdentity())) {
connection.hDel(PSKID_SEC.getBytes(), previousIdentity.getBytes());
}
return previous;
}
} }
@Override @Override
public SecurityInfo remove(String endpoint, boolean infosAreCompromised) { public void remove(String endpoint) {
try (var connection = connectionFactory.getConnection()) { //TODO: implement
byte[] data = connection.get((SEC_EP + endpoint).getBytes());
if (data != null) {
SecurityInfo info = deserialize(data);
if (info.getIdentity() != null) {
connection.hDel(PSKID_SEC.getBytes(), info.getIdentity().getBytes());
}
connection.del((SEC_EP + endpoint).getBytes());
if (listener != null) {
listener.securityInfoRemoved(infosAreCompromised, info);
}
return info;
}
}
return null;
} }
// @Override
// public Collection<SecurityInfo> getAll() {
// try (var connection = connectionFactory.getConnection()) {
// Collection<SecurityInfo> list = new LinkedList<>();
// ScanOptions scanOptions = ScanOptions.scanOptions().count(100).match(SEC_EP + "*").build();
// List<Cursor<byte[]>> scans = new ArrayList<>();
// if (connection instanceof RedisClusterConnection) {
// ((RedisClusterConnection) connection).clusterGetNodes().forEach(node -> {
// scans.add(((RedisClusterConnection) connection).scan(node, scanOptions));
// });
// } else {
// scans.add(connection.scan(scanOptions));
// }
//
// scans.forEach(scan -> {
// scan.forEachRemaining(key -> {
// byte[] element = connection.get(key);
// list.add(deserialize(element));
// });
// });
// return list;
// }
// }
//
// @Override
// public SecurityInfo add(SecurityInfo info) throws NonUniqueSecurityInfoException {
// byte[] data = serialize(info);
// try (var connection = connectionFactory.getConnection()) {
// if (info.getIdentity() != null) {
// // populate the secondary index (security info by PSK id)
// String oldEndpoint = new String(connection.hGet(PSKID_SEC.getBytes(), info.getIdentity().getBytes()));
// if (!oldEndpoint.equals(info.getEndpoint())) {
// throw new NonUniqueSecurityInfoException("PSK Identity " + info.getIdentity() + " is already used");
// }
// connection.hSet(PSKID_SEC.getBytes(), info.getIdentity().getBytes(), info.getEndpoint().getBytes());
// }
//
// byte[] previousData = connection.getSet((SEC_EP + info.getEndpoint()).getBytes(), data);
// SecurityInfo previous = previousData == null ? null : deserialize(previousData);
// String previousIdentity = previous == null ? null : previous.getIdentity();
// if (previousIdentity != null && !previousIdentity.equals(info.getIdentity())) {
// connection.hDel(PSKID_SEC.getBytes(), previousIdentity.getBytes());
// }
//
// return previous;
// }
// }
//
// @Override
// public SecurityInfo remove(String endpoint, boolean infosAreCompromised) {
// try (var connection = connectionFactory.getConnection()) {
// byte[] data = connection.get((SEC_EP + endpoint).getBytes());
//
// if (data != null) {
// SecurityInfo info = deserialize(data);
// if (info.getIdentity() != null) {
// connection.hDel(PSKID_SEC.getBytes(), info.getIdentity().getBytes());
// }
// connection.del((SEC_EP + endpoint).getBytes());
// if (listener != null) {
// listener.securityInfoRemoved(infosAreCompromised, info);
// }
// return info;
// }
// }
// return null;
// }
private byte[] serialize(SecurityInfo secInfo) { private byte[] serialize(SecurityInfo secInfo) {
return SecurityInfoSerDes.serialize(secInfo); return SecurityInfoSerDes.serialize(secInfo);
} }
@ -147,8 +164,4 @@ public class TbLwM2mRedisSecurityStore implements EditableSecurityStore {
return SecurityInfoSerDes.deserialize(data); return SecurityInfoSerDes.deserialize(data);
} }
@Override
public void setListener(SecurityStoreListener listener) {
this.listener = listener;
}
} }

82
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java

@ -19,63 +19,39 @@ import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.server.security.EditableSecurityStore; import org.eclipse.leshan.server.security.EditableSecurityStore;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException; import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.eclipse.leshan.server.security.SecurityInfo; import org.eclipse.leshan.server.security.SecurityInfo;
import org.eclipse.leshan.server.security.SecurityStore;
import org.eclipse.leshan.server.security.SecurityStoreListener; import org.eclipse.leshan.server.security.SecurityStoreListener;
import org.jetbrains.annotations.Nullable;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import java.util.Collection; import java.util.Collection;
@Slf4j @Slf4j
@Component public class TbLwM2mSecurityStore implements TbEditableSecurityStore {
@TbLwM2mTransportComponent
public class TbLwM2mSecurityStore implements EditableSecurityStore {
private final LwM2mClientContext clientContext; private final TbEditableSecurityStore securityStore;
private final EditableSecurityStore securityStore; private final LwM2mCredentialsSecurityInfoValidator validator;
public TbLwM2mSecurityStore(LwM2mClientContext clientContext, EditableSecurityStore securityStore) { public TbLwM2mSecurityStore(TbEditableSecurityStore securityStore, LwM2mCredentialsSecurityInfoValidator validator) {
this.clientContext = clientContext;
this.securityStore = securityStore; this.securityStore = securityStore;
this.validator = validator;
} }
@Override @Override
public Collection<SecurityInfo> getAll() { public TbLwM2MSecurityInfo getTbLwM2MSecurityInfoByEndpoint(String endpoint) {
return securityStore.getAll(); return securityStore.getTbLwM2MSecurityInfoByEndpoint(endpoint);
}
@Override
public SecurityInfo add(SecurityInfo info) throws NonUniqueSecurityInfoException {
return securityStore.add(info);
}
@Override
public SecurityInfo remove(String endpoint, boolean infosAreCompromised) {
return securityStore.remove(endpoint, infosAreCompromised);
}
@Override
public void setListener(SecurityStoreListener listener) {
securityStore.setListener(listener);
} }
@Override @Override
public SecurityInfo getByEndpoint(String endpoint) { public SecurityInfo getByEndpoint(String endpoint) {
SecurityInfo securityInfo = securityStore.getByEndpoint(endpoint); SecurityInfo securityInfo = securityStore.getByEndpoint(endpoint);
if (securityInfo == null) { if (securityInfo == null) {
LwM2mClient lwM2mClient = clientContext.getClientByEndpoint(endpoint); securityInfo = fetchAndPutSecurityInfo(endpoint);
if (lwM2mClient != null && lwM2mClient.getRegistration() != null && !lwM2mClient.getRegistration().getIdentity().isSecure()) {
return null;
}
securityInfo = clientContext.fetchClientByEndpoint(endpoint).getSecurityInfo();
try {
if (securityInfo != null) {
add(securityInfo);
}
} catch (NonUniqueSecurityInfoException e) {
log.trace("Failed to add security info: {}", securityInfo, e);
}
} }
return securityInfo; return securityInfo;
} }
@ -84,15 +60,31 @@ public class TbLwM2mSecurityStore implements EditableSecurityStore {
public SecurityInfo getByIdentity(String pskIdentity) { public SecurityInfo getByIdentity(String pskIdentity) {
SecurityInfo securityInfo = securityStore.getByIdentity(pskIdentity); SecurityInfo securityInfo = securityStore.getByIdentity(pskIdentity);
if (securityInfo == null) { if (securityInfo == null) {
securityInfo = clientContext.fetchClientByEndpoint(pskIdentity).getSecurityInfo(); securityInfo = fetchAndPutSecurityInfo(pskIdentity);
try {
if (securityInfo != null) {
add(securityInfo);
}
} catch (NonUniqueSecurityInfoException e) {
log.trace("Failed to add security info: {}", securityInfo, e);
}
} }
return securityInfo; return securityInfo;
} }
@Nullable
public SecurityInfo fetchAndPutSecurityInfo(String credentialsId) {
TbLwM2MSecurityInfo securityInfo = validator.getEndpointSecurityInfoByCredentialsId(credentialsId, LwM2mTransportUtil.LwM2mTypeServer.CLIENT);
try {
if (securityInfo != null) {
securityStore.put(securityInfo);
}
} catch (NonUniqueSecurityInfoException e) {
log.trace("Failed to add security info: {}", securityInfo, e);
}
return securityInfo != null ? securityInfo.getSecurityInfo() : null;
}
@Override
public void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException {
securityStore.put(tbSecurityInfo);
}
@Override
public void remove(String endpoint) {
securityStore.remove(endpoint);
}
} }

14
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mStoreFactory.java

@ -17,17 +17,14 @@ package org.thingsboard.server.transport.lwm2m.server.store;
import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore; import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore;
import org.eclipse.leshan.server.californium.registration.InMemoryRegistrationStore; import org.eclipse.leshan.server.californium.registration.InMemoryRegistrationStore;
import org.eclipse.leshan.server.security.EditableSecurityStore;
import org.eclipse.leshan.server.security.InMemorySecurityStore;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.cache.TBRedisCacheConfiguration; import org.thingsboard.server.cache.TBRedisCacheConfiguration;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
import java.util.Optional; import java.util.Optional;
@ -42,8 +39,7 @@ public class TbLwM2mStoreFactory {
private LwM2MTransportServerConfig config; private LwM2MTransportServerConfig config;
@Autowired @Autowired
@Lazy private LwM2mCredentialsSecurityInfoValidator validator;
private LwM2mClientContext clientContext;
@Value("${transport.lwm2m.redis.enabled:false}") @Value("${transport.lwm2m.redis.enabled:false}")
private boolean useRedis; private boolean useRedis;
@ -55,9 +51,9 @@ public class TbLwM2mStoreFactory {
} }
@Bean @Bean
private EditableSecurityStore securityStore() { private TbEditableSecurityStore securityStore() {
return new TbLwM2mSecurityStore(clientContext, redisConfiguration.isPresent() && useRedis ? return new TbLwM2mSecurityStore(redisConfiguration.isPresent() && useRedis ?
new TbLwM2mRedisSecurityStore(redisConfiguration.get().redisConnectionFactory()) : new InMemorySecurityStore()); new TbLwM2mRedisSecurityStore(redisConfiguration.get().redisConnectionFactory()) : new TbInMemorySecurityStore(), validator);
} }
@Bean @Bean

25
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbSecurityStore.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.transport.lwm2m.server.store;
import org.eclipse.leshan.server.security.SecurityStore;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
public interface TbSecurityStore extends SecurityStore {
TbLwM2MSecurityInfo getTbLwM2MSecurityInfoByEndpoint(String endpoint);
}

6
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java

@ -260,8 +260,10 @@ public class GatewaySessionHandler {
transportService.process(TransportProtos.TransportToDeviceActorMsg.newBuilder() transportService.process(TransportProtos.TransportToDeviceActorMsg.newBuilder()
.setSessionInfo(deviceSessionInfo) .setSessionInfo(deviceSessionInfo)
.setSessionEvent(DefaultTransportService.getSessionEventMsg(TransportProtos.SessionEvent.OPEN)) .setSessionEvent(DefaultTransportService.getSessionEventMsg(TransportProtos.SessionEvent.OPEN))
.setSubscribeToAttributes(TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build()) .setSubscribeToAttributes(TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder()
.setSubscribeToRPC(TransportProtos.SubscribeToRPCMsg.newBuilder().build()) .setSessionType(TransportProtos.SessionType.ASYNC).build())
.setSubscribeToRPC(TransportProtos.SubscribeToRPCMsg.newBuilder()
.setSessionType(TransportProtos.SessionType.ASYNC).build())
.build(), null); .build(), null);
} }
futureToSet.set(devices.get(deviceName)); futureToSet.set(devices.get(deviceName));

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

@ -408,10 +408,20 @@ public class ModelConstants {
/** /**
* OAuth2 client registration constants. * OAuth2 client registration constants.
*/ */
public static final String OAUTH2_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
public static final String OAUTH2_PARAMS_COLUMN_FAMILY_NAME = "oauth2_params";
public static final String OAUTH2_PARAMS_ENABLED_PROPERTY = "enabled";
public static final String OAUTH2_PARAMS_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
public static final String OAUTH2_REGISTRATION_COLUMN_FAMILY_NAME = "oauth2_registration";
public static final String OAUTH2_DOMAIN_COLUMN_FAMILY_NAME = "oauth2_domain";
public static final String OAUTH2_MOBILE_COLUMN_FAMILY_NAME = "oauth2_mobile";
public static final String OAUTH2_PARAMS_ID_PROPERTY = "oauth2_params_id";
public static final String OAUTH2_PKG_NAME_PROPERTY = "pkg_name";
public static final String OAUTH2_CALLBACK_URL_SCHEME_PROPERTY = "callback_url_scheme";
public static final String OAUTH2_CLIENT_REGISTRATION_INFO_COLUMN_FAMILY_NAME = "oauth2_client_registration_info"; public static final String OAUTH2_CLIENT_REGISTRATION_INFO_COLUMN_FAMILY_NAME = "oauth2_client_registration_info";
public static final String OAUTH2_CLIENT_REGISTRATION_COLUMN_FAMILY_NAME = "oauth2_client_registration"; public static final String OAUTH2_CLIENT_REGISTRATION_COLUMN_FAMILY_NAME = "oauth2_client_registration";
public static final String OAUTH2_CLIENT_REGISTRATION_TO_DOMAIN_COLUMN_FAMILY_NAME = "oauth2_client_registration_to_domain";
public static final String OAUTH2_CLIENT_REGISTRATION_TEMPLATE_COLUMN_FAMILY_NAME = "oauth2_client_registration_template"; public static final String OAUTH2_CLIENT_REGISTRATION_TEMPLATE_COLUMN_FAMILY_NAME = "oauth2_client_registration_template";
public static final String OAUTH2_ENABLED_PROPERTY = "enabled"; public static final String OAUTH2_ENABLED_PROPERTY = "enabled";
public static final String OAUTH2_TEMPLATE_PROVIDER_ID_PROPERTY = "provider_id"; public static final String OAUTH2_TEMPLATE_PROVIDER_ID_PROPERTY = "provider_id";
@ -422,8 +432,8 @@ public class ModelConstants {
public static final String OAUTH2_CLIENT_SECRET_PROPERTY = "client_secret"; public static final String OAUTH2_CLIENT_SECRET_PROPERTY = "client_secret";
public static final String OAUTH2_AUTHORIZATION_URI_PROPERTY = "authorization_uri"; public static final String OAUTH2_AUTHORIZATION_URI_PROPERTY = "authorization_uri";
public static final String OAUTH2_TOKEN_URI_PROPERTY = "token_uri"; public static final String OAUTH2_TOKEN_URI_PROPERTY = "token_uri";
public static final String OAUTH2_REDIRECT_URI_TEMPLATE_PROPERTY = "redirect_uri_template";
public static final String OAUTH2_SCOPE_PROPERTY = "scope"; public static final String OAUTH2_SCOPE_PROPERTY = "scope";
public static final String OAUTH2_PLATFORMS_PROPERTY = "platforms";
public static final String OAUTH2_USER_INFO_URI_PROPERTY = "user_info_uri"; public static final String OAUTH2_USER_INFO_URI_PROPERTY = "user_info_uri";
public static final String OAUTH2_USER_NAME_ATTRIBUTE_NAME_PROPERTY = "user_name_attribute_name"; public static final String OAUTH2_USER_NAME_ATTRIBUTE_NAME_PROPERTY = "user_name_attribute_name";
public static final String OAUTH2_JWK_SET_URI_PROPERTY = "jwk_set_uri"; public static final String OAUTH2_JWK_SET_URI_PROPERTY = "jwk_set_uri";

76
dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2DomainEntity.java

@ -0,0 +1,76 @@
/**
* 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.dao.model.sql;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.id.OAuth2DomainId;
import org.thingsboard.server.common.data.id.OAuth2ParamsId;
import org.thingsboard.server.common.data.oauth2.OAuth2Domain;
import org.thingsboard.server.common.data.oauth2.SchemeType;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Table;
import java.util.UUID;
@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = ModelConstants.OAUTH2_DOMAIN_COLUMN_FAMILY_NAME)
public class OAuth2DomainEntity extends BaseSqlEntity<OAuth2Domain> {
@Column(name = ModelConstants.OAUTH2_PARAMS_ID_PROPERTY)
private UUID oauth2ParamsId;
@Column(name = ModelConstants.OAUTH2_DOMAIN_NAME_PROPERTY)
private String domainName;
@Enumerated(EnumType.STRING)
@Column(name = ModelConstants.OAUTH2_DOMAIN_SCHEME_PROPERTY)
private SchemeType domainScheme;
public OAuth2DomainEntity() {
super();
}
public OAuth2DomainEntity(OAuth2Domain domain) {
if (domain.getId() != null) {
this.setUuid(domain.getId().getId());
}
this.setCreatedTime(domain.getCreatedTime());
if (domain.getOauth2ParamsId() != null) {
this.oauth2ParamsId = domain.getOauth2ParamsId().getId();
}
this.domainName = domain.getDomainName();
this.domainScheme = domain.getDomainScheme();
}
@Override
public OAuth2Domain toData() {
OAuth2Domain domain = new OAuth2Domain();
domain.setId(new OAuth2DomainId(id));
domain.setCreatedTime(createdTime);
domain.setOauth2ParamsId(new OAuth2ParamsId(oauth2ParamsId));
domain.setDomainName(domainName);
domain.setDomainScheme(domainScheme);
return domain;
}
}

72
dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MobileEntity.java

@ -0,0 +1,72 @@
/**
* 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.dao.model.sql;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.id.OAuth2MobileId;
import org.thingsboard.server.common.data.id.OAuth2ParamsId;
import org.thingsboard.server.common.data.oauth2.OAuth2Mobile;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.util.UUID;
@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = ModelConstants.OAUTH2_MOBILE_COLUMN_FAMILY_NAME)
public class OAuth2MobileEntity extends BaseSqlEntity<OAuth2Mobile> {
@Column(name = ModelConstants.OAUTH2_PARAMS_ID_PROPERTY)
private UUID oauth2ParamsId;
@Column(name = ModelConstants.OAUTH2_PKG_NAME_PROPERTY)
private String pkgName;
@Column(name = ModelConstants.OAUTH2_CALLBACK_URL_SCHEME_PROPERTY)
private String callbackUrlScheme;
public OAuth2MobileEntity() {
super();
}
public OAuth2MobileEntity(OAuth2Mobile mobile) {
if (mobile.getId() != null) {
this.setUuid(mobile.getId().getId());
}
this.setCreatedTime(mobile.getCreatedTime());
if (mobile.getOauth2ParamsId() != null) {
this.oauth2ParamsId = mobile.getOauth2ParamsId().getId();
}
this.pkgName = mobile.getPkgName();
this.callbackUrlScheme = mobile.getCallbackUrlScheme();
}
@Override
public OAuth2Mobile toData() {
OAuth2Mobile mobile = new OAuth2Mobile();
mobile.setId(new OAuth2MobileId(id));
mobile.setCreatedTime(createdTime);
mobile.setOauth2ParamsId(new OAuth2ParamsId(oauth2ParamsId));
mobile.setPkgName(pkgName);
mobile.setCallbackUrlScheme(callbackUrlScheme);
return mobile;
}
}

65
dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java

@ -0,0 +1,65 @@
/**
* 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.dao.model.sql;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.id.OAuth2ParamsId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.oauth2.OAuth2Params;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.util.UUID;
@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = ModelConstants.OAUTH2_PARAMS_COLUMN_FAMILY_NAME)
@NoArgsConstructor
public class OAuth2ParamsEntity extends BaseSqlEntity<OAuth2Params> {
@Column(name = ModelConstants.OAUTH2_PARAMS_ENABLED_PROPERTY)
private Boolean enabled;
@Column(name = ModelConstants.OAUTH2_PARAMS_TENANT_ID_PROPERTY)
private UUID tenantId;
public OAuth2ParamsEntity(OAuth2Params oauth2Params) {
if (oauth2Params.getId() != null) {
this.setUuid(oauth2Params.getUuidId());
}
this.setCreatedTime(oauth2Params.getCreatedTime());
this.enabled = oauth2Params.isEnabled();
if (oauth2Params.getTenantId() != null) {
this.tenantId = oauth2Params.getTenantId().getId();
}
}
@Override
public OAuth2Params toData() {
OAuth2Params oauth2Params = new OAuth2Params();
oauth2Params.setId(new OAuth2ParamsId(id));
oauth2Params.setCreatedTime(createdTime);
oauth2Params.setTenantId(new TenantId(tenantId));
oauth2Params.setEnabled(enabled);
return oauth2Params;
}
}

221
dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationEntity.java

@ -0,0 +1,221 @@
/**
* 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.dao.model.sql;
import com.fasterxml.jackson.databind.JsonNode;
import io.micrometer.core.instrument.util.StringUtils;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.thingsboard.server.common.data.id.OAuth2ParamsId;
import org.thingsboard.server.common.data.id.OAuth2RegistrationId;
import org.thingsboard.server.common.data.oauth2.MapperType;
import org.thingsboard.server.common.data.oauth2.OAuth2BasicMapperConfig;
import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig;
import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
import org.thingsboard.server.common.data.oauth2.OAuth2Registration;
import org.thingsboard.server.common.data.oauth2.PlatformType;
import org.thingsboard.server.common.data.oauth2.TenantNameStrategyType;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants;
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 java.util.Arrays;
import java.util.Collections;
import java.util.UUID;
import java.util.stream.Collectors;
@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@TypeDef(name = "json", typeClass = JsonStringType.class)
@Table(name = ModelConstants.OAUTH2_REGISTRATION_COLUMN_FAMILY_NAME)
public class OAuth2RegistrationEntity extends BaseSqlEntity<OAuth2Registration> {
@Column(name = ModelConstants.OAUTH2_PARAMS_ID_PROPERTY)
private UUID oauth2ParamsId;
@Column(name = ModelConstants.OAUTH2_CLIENT_ID_PROPERTY)
private String clientId;
@Column(name = ModelConstants.OAUTH2_CLIENT_SECRET_PROPERTY)
private String clientSecret;
@Column(name = ModelConstants.OAUTH2_AUTHORIZATION_URI_PROPERTY)
private String authorizationUri;
@Column(name = ModelConstants.OAUTH2_TOKEN_URI_PROPERTY)
private String tokenUri;
@Column(name = ModelConstants.OAUTH2_SCOPE_PROPERTY)
private String scope;
@Column(name = ModelConstants.OAUTH2_PLATFORMS_PROPERTY)
private String platforms;
@Column(name = ModelConstants.OAUTH2_USER_INFO_URI_PROPERTY)
private String userInfoUri;
@Column(name = ModelConstants.OAUTH2_USER_NAME_ATTRIBUTE_NAME_PROPERTY)
private String userNameAttributeName;
@Column(name = ModelConstants.OAUTH2_JWK_SET_URI_PROPERTY)
private String jwkSetUri;
@Column(name = ModelConstants.OAUTH2_CLIENT_AUTHENTICATION_METHOD_PROPERTY)
private String clientAuthenticationMethod;
@Column(name = ModelConstants.OAUTH2_LOGIN_BUTTON_LABEL_PROPERTY)
private String loginButtonLabel;
@Column(name = ModelConstants.OAUTH2_LOGIN_BUTTON_ICON_PROPERTY)
private String loginButtonIcon;
@Column(name = ModelConstants.OAUTH2_ALLOW_USER_CREATION_PROPERTY)
private Boolean allowUserCreation;
@Column(name = ModelConstants.OAUTH2_ACTIVATE_USER_PROPERTY)
private Boolean activateUser;
@Enumerated(EnumType.STRING)
@Column(name = ModelConstants.OAUTH2_MAPPER_TYPE_PROPERTY)
private MapperType type;
@Column(name = ModelConstants.OAUTH2_EMAIL_ATTRIBUTE_KEY_PROPERTY)
private String emailAttributeKey;
@Column(name = ModelConstants.OAUTH2_FIRST_NAME_ATTRIBUTE_KEY_PROPERTY)
private String firstNameAttributeKey;
@Column(name = ModelConstants.OAUTH2_LAST_NAME_ATTRIBUTE_KEY_PROPERTY)
private String lastNameAttributeKey;
@Enumerated(EnumType.STRING)
@Column(name = ModelConstants.OAUTH2_TENANT_NAME_STRATEGY_PROPERTY)
private TenantNameStrategyType tenantNameStrategy;
@Column(name = ModelConstants.OAUTH2_TENANT_NAME_PATTERN_PROPERTY)
private String tenantNamePattern;
@Column(name = ModelConstants.OAUTH2_CUSTOMER_NAME_PATTERN_PROPERTY)
private String customerNamePattern;
@Column(name = ModelConstants.OAUTH2_DEFAULT_DASHBOARD_NAME_PROPERTY)
private String defaultDashboardName;
@Column(name = ModelConstants.OAUTH2_ALWAYS_FULL_SCREEN_PROPERTY)
private Boolean alwaysFullScreen;
@Column(name = ModelConstants.OAUTH2_MAPPER_URL_PROPERTY)
private String url;
@Column(name = ModelConstants.OAUTH2_MAPPER_USERNAME_PROPERTY)
private String username;
@Column(name = ModelConstants.OAUTH2_MAPPER_PASSWORD_PROPERTY)
private String password;
@Column(name = ModelConstants.OAUTH2_MAPPER_SEND_TOKEN_PROPERTY)
private Boolean sendToken;
@Type(type = "json")
@Column(name = ModelConstants.OAUTH2_ADDITIONAL_INFO_PROPERTY)
private JsonNode additionalInfo;
public OAuth2RegistrationEntity() {
super();
}
public OAuth2RegistrationEntity(OAuth2Registration registration) {
if (registration.getId() != null) {
this.setUuid(registration.getId().getId());
}
this.setCreatedTime(registration.getCreatedTime());
if (registration.getOauth2ParamsId() != null) {
this.oauth2ParamsId = registration.getOauth2ParamsId().getId();
}
this.clientId = registration.getClientId();
this.clientSecret = registration.getClientSecret();
this.authorizationUri = registration.getAuthorizationUri();
this.tokenUri = registration.getAccessTokenUri();
this.scope = registration.getScope().stream().reduce((result, element) -> result + "," + element).orElse("");
this.platforms = registration.getPlatforms() != null ? registration.getPlatforms().stream().map(Enum::name).reduce((result, element) -> result + "," + element).orElse("") : "";
this.userInfoUri = registration.getUserInfoUri();
this.userNameAttributeName = registration.getUserNameAttributeName();
this.jwkSetUri = registration.getJwkSetUri();
this.clientAuthenticationMethod = registration.getClientAuthenticationMethod();
this.loginButtonLabel = registration.getLoginButtonLabel();
this.loginButtonIcon = registration.getLoginButtonIcon();
this.additionalInfo = registration.getAdditionalInfo();
OAuth2MapperConfig mapperConfig = registration.getMapperConfig();
if (mapperConfig != null) {
this.allowUserCreation = mapperConfig.isAllowUserCreation();
this.activateUser = mapperConfig.isActivateUser();
this.type = mapperConfig.getType();
OAuth2BasicMapperConfig basicConfig = mapperConfig.getBasic();
if (basicConfig != null) {
this.emailAttributeKey = basicConfig.getEmailAttributeKey();
this.firstNameAttributeKey = basicConfig.getFirstNameAttributeKey();
this.lastNameAttributeKey = basicConfig.getLastNameAttributeKey();
this.tenantNameStrategy = basicConfig.getTenantNameStrategy();
this.tenantNamePattern = basicConfig.getTenantNamePattern();
this.customerNamePattern = basicConfig.getCustomerNamePattern();
this.defaultDashboardName = basicConfig.getDefaultDashboardName();
this.alwaysFullScreen = basicConfig.isAlwaysFullScreen();
}
OAuth2CustomMapperConfig customConfig = mapperConfig.getCustom();
if (customConfig != null) {
this.url = customConfig.getUrl();
this.username = customConfig.getUsername();
this.password = customConfig.getPassword();
this.sendToken = customConfig.isSendToken();
}
}
}
@Override
public OAuth2Registration toData() {
OAuth2Registration registration = new OAuth2Registration();
registration.setId(new OAuth2RegistrationId(id));
registration.setCreatedTime(createdTime);
registration.setOauth2ParamsId(new OAuth2ParamsId(oauth2ParamsId));
registration.setAdditionalInfo(additionalInfo);
registration.setMapperConfig(
OAuth2MapperConfig.builder()
.allowUserCreation(allowUserCreation)
.activateUser(activateUser)
.type(type)
.basic(
(type == MapperType.BASIC || type == MapperType.GITHUB) ?
OAuth2BasicMapperConfig.builder()
.emailAttributeKey(emailAttributeKey)
.firstNameAttributeKey(firstNameAttributeKey)
.lastNameAttributeKey(lastNameAttributeKey)
.tenantNameStrategy(tenantNameStrategy)
.tenantNamePattern(tenantNamePattern)
.customerNamePattern(customerNamePattern)
.defaultDashboardName(defaultDashboardName)
.alwaysFullScreen(alwaysFullScreen)
.build()
: null
)
.custom(
type == MapperType.CUSTOM ?
OAuth2CustomMapperConfig.builder()
.url(url)
.username(username)
.password(password)
.sendToken(sendToken)
.build()
: null
)
.build()
);
registration.setClientId(clientId);
registration.setClientSecret(clientSecret);
registration.setAuthorizationUri(authorizationUri);
registration.setAccessTokenUri(tokenUri);
registration.setScope(Arrays.asList(scope.split(",")));
registration.setPlatforms(StringUtils.isNotEmpty(platforms) ? Arrays.stream(platforms.split(","))
.map(str -> PlatformType.valueOf(str)).collect(Collectors.toList()) : Collections.emptyList());
registration.setUserInfoUri(userInfoUri);
registration.setUserNameAttributeName(userNameAttributeName);
registration.setJwkSetUri(jwkSetUri);
registration.setClientAuthenticationMethod(clientAuthenticationMethod);
registration.setLoginButtonLabel(loginButtonLabel);
registration.setLoginButtonIcon(loginButtonIcon);
return registration;
}
}

7
dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractOAuth2ClientRegistrationInfoEntity.java → dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/AbstractOAuth2ClientRegistrationInfoEntity.java

@ -13,22 +13,25 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.dao.model.sql; package org.thingsboard.server.dao.model.sql.deprecated;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Type; import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef; import org.hibernate.annotations.TypeDef;
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationInfoId;
import org.thingsboard.server.common.data.oauth2.*; import org.thingsboard.server.common.data.oauth2.*;
import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo;
import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.model.sql.deprecated.OAuth2ClientRegistrationInfoEntity;
import org.thingsboard.server.dao.util.mapping.JsonStringType; import org.thingsboard.server.dao.util.mapping.JsonStringType;
import javax.persistence.*; import javax.persistence.*;
import java.util.Arrays; import java.util.Arrays;
@Deprecated
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@TypeDef(name = "json", typeClass = JsonStringType.class) @TypeDef(name = "json", typeClass = JsonStringType.class)

5
dao/src/main/java/org/thingsboard/server/dao/model/sql/ExtendedOAuth2ClientRegistrationInfoEntity.java → dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/ExtendedOAuth2ClientRegistrationInfoEntity.java

@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.dao.model.sql; package org.thingsboard.server.dao.model.sql.deprecated;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.oauth2.ExtendedOAuth2ClientRegistrationInfo; import org.thingsboard.server.common.data.oauth2.deprecated.ExtendedOAuth2ClientRegistrationInfo;
import org.thingsboard.server.common.data.oauth2.SchemeType; import org.thingsboard.server.common.data.oauth2.SchemeType;
@Deprecated
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class ExtendedOAuth2ClientRegistrationInfoEntity extends AbstractOAuth2ClientRegistrationInfoEntity<ExtendedOAuth2ClientRegistrationInfo> { public class ExtendedOAuth2ClientRegistrationInfoEntity extends AbstractOAuth2ClientRegistrationInfoEntity<ExtendedOAuth2ClientRegistrationInfo> {

9
dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java → dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/OAuth2ClientRegistrationEntity.java

@ -13,22 +13,23 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.dao.model.sql; package org.thingsboard.server.dao.model.sql.deprecated;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.hibernate.annotations.TypeDef; import org.hibernate.annotations.TypeDef;
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationId;
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationInfoId;
import org.thingsboard.server.common.data.oauth2.*; import org.thingsboard.server.common.data.oauth2.*;
import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistration;
import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.util.mapping.JsonStringType; import org.thingsboard.server.dao.util.mapping.JsonStringType;
import javax.persistence.*; import javax.persistence.*;
import java.util.Arrays;
import java.util.UUID; import java.util.UUID;
@Deprecated
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Entity @Entity

5
dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationInfoEntity.java → dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/OAuth2ClientRegistrationInfoEntity.java

@ -13,18 +13,19 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.dao.model.sql; package org.thingsboard.server.dao.model.sql.deprecated;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.hibernate.annotations.TypeDef; import org.hibernate.annotations.TypeDef;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.util.mapping.JsonStringType; import org.thingsboard.server.dao.util.mapping.JsonStringType;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Table; import javax.persistence.Table;
@Deprecated
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Entity @Entity

32
dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java

@ -21,7 +21,7 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio
import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; import org.thingsboard.server.common.data.oauth2.OAuth2Registration;
import java.util.UUID; import java.util.UUID;
@ -34,25 +34,25 @@ public class HybridClientRegistrationRepository implements ClientRegistrationRep
@Override @Override
public ClientRegistration findByRegistrationId(String registrationId) { public ClientRegistration findByRegistrationId(String registrationId) {
OAuth2ClientRegistrationInfo oAuth2ClientRegistrationInfo = oAuth2Service.findClientRegistrationInfo(UUID.fromString(registrationId)); OAuth2Registration registration = oAuth2Service.findRegistration(UUID.fromString(registrationId));
return oAuth2ClientRegistrationInfo == null ? return registration == null ?
null : toSpringClientRegistration(oAuth2ClientRegistrationInfo); null : toSpringClientRegistration(registration);
} }
private ClientRegistration toSpringClientRegistration(OAuth2ClientRegistrationInfo localClientRegistration){ private ClientRegistration toSpringClientRegistration(OAuth2Registration registration){
String registrationId = localClientRegistration.getUuidId().toString(); String registrationId = registration.getUuidId().toString();
return ClientRegistration.withRegistrationId(registrationId) return ClientRegistration.withRegistrationId(registrationId)
.clientName(localClientRegistration.getName()) .clientName(registration.getName())
.clientId(localClientRegistration.getClientId()) .clientId(registration.getClientId())
.authorizationUri(localClientRegistration.getAuthorizationUri()) .authorizationUri(registration.getAuthorizationUri())
.clientSecret(localClientRegistration.getClientSecret()) .clientSecret(registration.getClientSecret())
.tokenUri(localClientRegistration.getAccessTokenUri()) .tokenUri(registration.getAccessTokenUri())
.scope(localClientRegistration.getScope()) .scope(registration.getScope())
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.userInfoUri(localClientRegistration.getUserInfoUri()) .userInfoUri(registration.getUserInfoUri())
.userNameAttributeName(localClientRegistration.getUserNameAttributeName()) .userNameAttributeName(registration.getUserNameAttributeName())
.jwkSetUri(localClientRegistration.getJwkSetUri()) .jwkSetUri(registration.getJwkSetUri())
.clientAuthenticationMethod(new ClientAuthenticationMethod(localClientRegistration.getClientAuthenticationMethod())) .clientAuthenticationMethod(new ClientAuthenticationMethod(registration.getClientAuthenticationMethod()))
.redirectUri(defaultRedirectUriTemplate) .redirectUri(defaultRedirectUriTemplate)
.build(); .build();
} }

28
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2DomainDao.java

@ -0,0 +1,28 @@
/**
* 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.dao.oauth2;
import org.thingsboard.server.common.data.oauth2.OAuth2Domain;
import org.thingsboard.server.dao.Dao;
import java.util.List;
import java.util.UUID;
public interface OAuth2DomainDao extends Dao<OAuth2Domain> {
List<OAuth2Domain> findByOAuth2ParamsId(UUID oauth2ParamsId);
}

28
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2MobileDao.java

@ -0,0 +1,28 @@
/**
* 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.dao.oauth2;
import org.thingsboard.server.common.data.oauth2.OAuth2Mobile;
import org.thingsboard.server.dao.Dao;
import java.util.List;
import java.util.UUID;
public interface OAuth2MobileDao extends Dao<OAuth2Mobile> {
List<OAuth2Mobile> findByOAuth2ParamsId(UUID oauth2ParamsId);
}

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

Loading…
Cancel
Save