Browse Source

merge with upstream

pull/1479/head
ShvaykaD 8 years ago
parent
commit
554da62831
  1. 2
      application/pom.xml
  2. 5
      application/src/main/java/org/thingsboard/server/controller/AdminController.java
  3. 16
      application/src/main/java/org/thingsboard/server/controller/AlarmController.java
  4. 37
      application/src/main/java/org/thingsboard/server/controller/AssetController.java
  5. 210
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  6. 14
      application/src/main/java/org/thingsboard/server/controller/CustomerController.java
  7. 36
      application/src/main/java/org/thingsboard/server/controller/DashboardController.java
  8. 41
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  9. 31
      application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
  10. 28
      application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
  11. 25
      application/src/main/java/org/thingsboard/server/controller/EventController.java
  12. 3
      application/src/main/java/org/thingsboard/server/controller/RpcController.java
  13. 31
      application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
  14. 23
      application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
  15. 11
      application/src/main/java/org/thingsboard/server/controller/TenantController.java
  16. 49
      application/src/main/java/org/thingsboard/server/controller/UserController.java
  17. 19
      application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java
  18. 14
      application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java
  19. 6
      application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
  20. 2
      application/src/main/java/org/thingsboard/server/service/install/update/DataUpdateService.java
  21. 28
      application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java
  22. 46
      application/src/main/java/org/thingsboard/server/service/install/update/PaginatedUpdater.java
  23. 127
      application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java
  24. 32
      application/src/main/java/org/thingsboard/server/service/security/permission/AbstractPermissions.java
  25. 31
      application/src/main/java/org/thingsboard/server/service/security/permission/AccessControlService.java
  26. 135
      application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPremissions.java
  27. 91
      application/src/main/java/org/thingsboard/server/service/security/permission/DefaultAccessControlService.java
  28. 23
      application/src/main/java/org/thingsboard/server/service/security/permission/Operation.java
  29. 73
      application/src/main/java/org/thingsboard/server/service/security/permission/PermissionChecker.java
  30. 24
      application/src/main/java/org/thingsboard/server/service/security/permission/Permissions.java
  31. 49
      application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java
  32. 68
      application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java
  33. 104
      application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java
  34. 17
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java
  35. 2
      common/data/pom.xml
  36. 2
      common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java
  37. 2
      common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java
  38. 9
      common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java
  39. 4
      common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java
  40. 9
      common/data/src/main/java/org/thingsboard/server/common/data/id/WidgetTypeId.java
  41. 9
      common/data/src/main/java/org/thingsboard/server/common/data/id/WidgetsBundleId.java
  42. 3
      common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java
  43. 3
      common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java
  44. 2
      common/message/pom.xml
  45. 2
      common/pom.xml
  46. 2
      common/queue/pom.xml
  47. 2
      common/transport/coap/pom.xml
  48. 2
      common/transport/http/pom.xml
  49. 2
      common/transport/mqtt/pom.xml
  50. 2
      common/transport/pom.xml
  51. 2
      common/transport/transport-api/pom.xml
  52. 2
      dao/pom.xml
  53. 91
      dao/src/main/java/org/thingsboard/server/dao/model/sql/TsKvEntity.java
  54. 18
      dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java
  55. 2
      dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java
  56. 3
      dao/src/test/resources/application-test.properties
  57. 4
      docker/docker-upgrade-tb.sh
  58. 3
      docker/tb-js-executor.env
  59. 2
      msa/black-box-tests/pom.xml
  60. 13
      msa/js-executor/api/jsInvokeMessageProcessor.js
  61. 3
      msa/js-executor/config/custom-environment-variables.yml
  62. 3
      msa/js-executor/config/default.yml
  63. 2
      msa/js-executor/package-lock.json
  64. 2
      msa/js-executor/package.json
  65. 2
      msa/js-executor/pom.xml
  66. 2
      msa/pom.xml
  67. 2
      msa/tb-node/pom.xml
  68. 4
      msa/tb/pom.xml
  69. 2
      msa/transport/coap/pom.xml
  70. 2
      msa/transport/http/pom.xml
  71. 2
      msa/transport/mqtt/pom.xml
  72. 2
      msa/transport/pom.xml
  73. 2
      msa/web-ui/package-lock.json
  74. 2
      msa/web-ui/package.json
  75. 2
      msa/web-ui/pom.xml
  76. 4
      netty-mqtt/pom.xml
  77. 2
      pom.xml
  78. 2
      rule-engine/pom.xml
  79. 2
      rule-engine/rule-engine-api/pom.xml
  80. 2
      rule-engine/rule-engine-components/pom.xml
  81. 12
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java
  82. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java
  83. 6
      rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
  84. 2
      tools/pom.xml
  85. 2
      transport/coap/pom.xml
  86. 2
      transport/http/pom.xml
  87. 2
      transport/mqtt/pom.xml
  88. 2
      transport/pom.xml
  89. 2
      ui/package-lock.json
  90. 2
      ui/package.json
  91. 2
      ui/pom.xml
  92. 1
      ui/src/app/locale/locale.constant-de_DE.json
  93. 1
      ui/src/app/locale/locale.constant-en_US.json
  94. 3
      ui/src/app/locale/locale.constant-es_ES.json
  95. 1
      ui/src/app/locale/locale.constant-fr_FR.json
  96. 304
      ui/src/app/locale/locale.constant-it_IT.json
  97. 1
      ui/src/app/locale/locale.constant-ja_JA.json
  98. 1
      ui/src/app/locale/locale.constant-ko_KR.json
  99. 1
      ui/src/app/locale/locale.constant-ru_RU.json
  100. 3
      ui/src/app/locale/locale.constant-tr_TR.json

2
application/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>application</artifactId> <artifactId>application</artifactId>

5
application/src/main/java/org/thingsboard/server/controller/AdminController.java

@ -28,6 +28,8 @@ import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import org.thingsboard.server.service.update.UpdateService; import org.thingsboard.server.service.update.UpdateService;
import org.thingsboard.server.service.update.model.UpdateMessage; import org.thingsboard.server.service.update.model.UpdateMessage;
@ -49,6 +51,7 @@ public class AdminController extends BaseController {
@ResponseBody @ResponseBody
public AdminSettings getAdminSettings(@PathVariable("key") String key) throws ThingsboardException { public AdminSettings getAdminSettings(@PathVariable("key") String key) throws ThingsboardException {
try { try {
accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ);
return checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, key)); return checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, key));
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
@ -60,6 +63,7 @@ public class AdminController extends BaseController {
@ResponseBody @ResponseBody
public AdminSettings saveAdminSettings(@RequestBody AdminSettings adminSettings) throws ThingsboardException { public AdminSettings saveAdminSettings(@RequestBody AdminSettings adminSettings) throws ThingsboardException {
try { try {
accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.WRITE);
adminSettings = checkNotNull(adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminSettings)); adminSettings = checkNotNull(adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminSettings));
if (adminSettings.getKey().equals("mail")) { if (adminSettings.getKey().equals("mail")) {
mailService.updateMailConfiguration(); mailService.updateMailConfiguration();
@ -74,6 +78,7 @@ public class AdminController extends BaseController {
@RequestMapping(value = "/settings/testMail", method = RequestMethod.POST) @RequestMapping(value = "/settings/testMail", method = RequestMethod.POST)
public void sendTestMail(@RequestBody AdminSettings adminSettings) throws ThingsboardException { public void sendTestMail(@RequestBody AdminSettings adminSettings) throws ThingsboardException {
try { try {
accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ);
adminSettings = checkNotNull(adminSettings); adminSettings = checkNotNull(adminSettings);
if (adminSettings.getKey().equals("mail")) { if (adminSettings.getKey().equals("mail")) {
String email = getCurrentUser().getEmail(); String email = getCurrentUser().getEmail();

16
application/src/main/java/org/thingsboard/server/controller/AlarmController.java

@ -41,6 +41,8 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
@RestController @RestController
@RequestMapping("/api") @RequestMapping("/api")
@ -55,7 +57,7 @@ public class AlarmController extends BaseController {
checkParameter(ALARM_ID, strAlarmId); checkParameter(ALARM_ID, strAlarmId);
try { try {
AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
return checkAlarmId(alarmId); return checkAlarmId(alarmId, Operation.READ);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -68,7 +70,7 @@ public class AlarmController extends BaseController {
checkParameter(ALARM_ID, strAlarmId); checkParameter(ALARM_ID, strAlarmId);
try { try {
AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
return checkAlarmInfoId(alarmId); return checkAlarmInfoId(alarmId, Operation.READ);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -80,6 +82,8 @@ public class AlarmController extends BaseController {
public Alarm saveAlarm(@RequestBody Alarm alarm) throws ThingsboardException { public Alarm saveAlarm(@RequestBody Alarm alarm) throws ThingsboardException {
try { try {
alarm.setTenantId(getCurrentUser().getTenantId()); alarm.setTenantId(getCurrentUser().getTenantId());
Operation operation = alarm.getId() == null ? Operation.CREATE : Operation.WRITE;
accessControlService.checkPermission(getCurrentUser(), Resource.ALARM, operation, alarm.getId(), alarm);
Alarm savedAlarm = checkNotNull(alarmService.createOrUpdateAlarm(alarm)); Alarm savedAlarm = checkNotNull(alarmService.createOrUpdateAlarm(alarm));
logEntityAction(savedAlarm.getId(), savedAlarm, logEntityAction(savedAlarm.getId(), savedAlarm,
getCurrentUser().getCustomerId(), getCurrentUser().getCustomerId(),
@ -112,7 +116,7 @@ public class AlarmController extends BaseController {
checkParameter(ALARM_ID, strAlarmId); checkParameter(ALARM_ID, strAlarmId);
try { try {
AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
Alarm alarm = checkAlarmId(alarmId); Alarm alarm = checkAlarmId(alarmId, Operation.WRITE);
alarmService.ackAlarm(getCurrentUser().getTenantId(), alarmId, System.currentTimeMillis()).get(); alarmService.ackAlarm(getCurrentUser().getTenantId(), alarmId, System.currentTimeMillis()).get();
logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_ACK, null); logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_ACK, null);
} catch (Exception e) { } catch (Exception e) {
@ -127,7 +131,7 @@ public class AlarmController extends BaseController {
checkParameter(ALARM_ID, strAlarmId); checkParameter(ALARM_ID, strAlarmId);
try { try {
AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
Alarm alarm = checkAlarmId(alarmId); Alarm alarm = checkAlarmId(alarmId, Operation.WRITE);
alarmService.clearAlarm(getCurrentUser().getTenantId(), alarmId, null, System.currentTimeMillis()).get(); alarmService.clearAlarm(getCurrentUser().getTenantId(), alarmId, null, System.currentTimeMillis()).get();
logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_CLEAR, null); logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_CLEAR, null);
} catch (Exception e) { } catch (Exception e) {
@ -159,7 +163,7 @@ public class AlarmController extends BaseController {
throw new ThingsboardException("Invalid alarms search query: Both parameters 'searchStatus' " + throw new ThingsboardException("Invalid alarms search query: Both parameters 'searchStatus' " +
"and 'status' can't be specified at the same time!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); "and 'status' can't be specified at the same time!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
} }
checkEntityId(entityId); checkEntityId(entityId, Operation.READ);
try { try {
TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get()); return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get());
@ -186,7 +190,7 @@ public class AlarmController extends BaseController {
throw new ThingsboardException("Invalid alarms search query: Both parameters 'searchStatus' " + throw new ThingsboardException("Invalid alarms search query: Both parameters 'searchStatus' " +
"and 'status' can't be specified at the same time!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); "and 'status' can't be specified at the same time!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
} }
checkEntityId(entityId); checkEntityId(entityId, Operation.READ);
try { try {
return alarmService.findHighestAlarmSeverity(getCurrentUser().getTenantId(), entityId, alarmSearchStatus, alarmStatus); return alarmService.findHighestAlarmSeverity(getCurrentUser().getTenantId(), entityId, alarmSearchStatus, alarmStatus);
} catch (Exception e) { } catch (Exception e) {

37
application/src/main/java/org/thingsboard/server/controller/AssetController.java

@ -43,6 +43,8 @@ import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -61,7 +63,7 @@ public class AssetController extends BaseController {
checkParameter(ASSET_ID, strAssetId); checkParameter(ASSET_ID, strAssetId);
try { try {
AssetId assetId = new AssetId(toUUID(strAssetId)); AssetId assetId = new AssetId(toUUID(strAssetId));
return checkAssetId(assetId); return checkAssetId(assetId, Operation.READ);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -73,15 +75,12 @@ public class AssetController extends BaseController {
public Asset saveAsset(@RequestBody Asset asset) throws ThingsboardException { public Asset saveAsset(@RequestBody Asset asset) throws ThingsboardException {
try { try {
asset.setTenantId(getCurrentUser().getTenantId()); asset.setTenantId(getCurrentUser().getTenantId());
if (getCurrentUser().getAuthority() == Authority.CUSTOMER_USER) {
if (asset.getId() == null || asset.getId().isNullUid() || Operation operation = asset.getId() == null ? Operation.CREATE : Operation.WRITE;
asset.getCustomerId() == null || asset.getCustomerId().isNullUid()) {
throw new ThingsboardException("You don't have permission to perform this operation!", accessControlService.checkPermission(getCurrentUser(), Resource.ASSET, operation,
ThingsboardErrorCode.PERMISSION_DENIED); asset.getId(), asset);
} else {
checkCustomerId(asset.getCustomerId());
}
}
Asset savedAsset = checkNotNull(assetService.saveAsset(asset)); Asset savedAsset = checkNotNull(assetService.saveAsset(asset));
logEntityAction(savedAsset.getId(), savedAsset, logEntityAction(savedAsset.getId(), savedAsset,
@ -103,7 +102,7 @@ public class AssetController extends BaseController {
checkParameter(ASSET_ID, strAssetId); checkParameter(ASSET_ID, strAssetId);
try { try {
AssetId assetId = new AssetId(toUUID(strAssetId)); AssetId assetId = new AssetId(toUUID(strAssetId));
Asset asset = checkAssetId(assetId); Asset asset = checkAssetId(assetId, Operation.DELETE);
assetService.deleteAsset(getTenantId(), assetId); assetService.deleteAsset(getTenantId(), assetId);
logEntityAction(assetId, asset, logEntityAction(assetId, asset,
@ -128,10 +127,10 @@ public class AssetController extends BaseController {
checkParameter(ASSET_ID, strAssetId); checkParameter(ASSET_ID, strAssetId);
try { try {
CustomerId customerId = new CustomerId(toUUID(strCustomerId)); CustomerId customerId = new CustomerId(toUUID(strCustomerId));
Customer customer = checkCustomerId(customerId); Customer customer = checkCustomerId(customerId, Operation.READ);
AssetId assetId = new AssetId(toUUID(strAssetId)); AssetId assetId = new AssetId(toUUID(strAssetId));
checkAssetId(assetId); checkAssetId(assetId, Operation.ASSIGN_TO_CUSTOMER);
Asset savedAsset = checkNotNull(assetService.assignAssetToCustomer(getTenantId(), assetId, customerId)); Asset savedAsset = checkNotNull(assetService.assignAssetToCustomer(getTenantId(), assetId, customerId));
@ -157,12 +156,12 @@ public class AssetController extends BaseController {
checkParameter(ASSET_ID, strAssetId); checkParameter(ASSET_ID, strAssetId);
try { try {
AssetId assetId = new AssetId(toUUID(strAssetId)); AssetId assetId = new AssetId(toUUID(strAssetId));
Asset asset = checkAssetId(assetId); Asset asset = checkAssetId(assetId, Operation.UNASSIGN_FROM_CUSTOMER);
if (asset.getCustomerId() == null || asset.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { if (asset.getCustomerId() == null || asset.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
throw new IncorrectParameterException("Asset isn't assigned to any customer!"); throw new IncorrectParameterException("Asset isn't assigned to any customer!");
} }
Customer customer = checkCustomerId(asset.getCustomerId()); Customer customer = checkCustomerId(asset.getCustomerId(), Operation.READ);
Asset savedAsset = checkNotNull(assetService.unassignAssetFromCustomer(getTenantId(), assetId)); Asset savedAsset = checkNotNull(assetService.unassignAssetFromCustomer(getTenantId(), assetId));
@ -188,7 +187,7 @@ public class AssetController extends BaseController {
checkParameter(ASSET_ID, strAssetId); checkParameter(ASSET_ID, strAssetId);
try { try {
AssetId assetId = new AssetId(toUUID(strAssetId)); AssetId assetId = new AssetId(toUUID(strAssetId));
Asset asset = checkAssetId(assetId); Asset asset = checkAssetId(assetId, Operation.ASSIGN_TO_CUSTOMER);
Customer publicCustomer = customerService.findOrCreatePublicCustomer(asset.getTenantId()); Customer publicCustomer = customerService.findOrCreatePublicCustomer(asset.getTenantId());
Asset savedAsset = checkNotNull(assetService.assignAssetToCustomer(getTenantId(), assetId, publicCustomer.getId())); Asset savedAsset = checkNotNull(assetService.assignAssetToCustomer(getTenantId(), assetId, publicCustomer.getId()));
@ -256,7 +255,7 @@ public class AssetController extends BaseController {
try { try {
TenantId tenantId = getCurrentUser().getTenantId(); TenantId tenantId = getCurrentUser().getTenantId();
CustomerId customerId = new CustomerId(toUUID(strCustomerId)); CustomerId customerId = new CustomerId(toUUID(strCustomerId));
checkCustomerId(customerId); checkCustomerId(customerId, Operation.READ);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
if (type != null && type.trim().length()>0) { if (type != null && type.trim().length()>0) {
return checkNotNull(assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); return checkNotNull(assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink));
@ -301,12 +300,12 @@ public class AssetController extends BaseController {
checkNotNull(query); checkNotNull(query);
checkNotNull(query.getParameters()); checkNotNull(query.getParameters());
checkNotNull(query.getAssetTypes()); checkNotNull(query.getAssetTypes());
checkEntityId(query.getParameters().getEntityId()); checkEntityId(query.getParameters().getEntityId(), Operation.READ);
try { try {
List<Asset> assets = checkNotNull(assetService.findAssetsByQuery(getTenantId(), query).get()); List<Asset> assets = checkNotNull(assetService.findAssetsByQuery(getTenantId(), query).get());
assets = assets.stream().filter(asset -> { assets = assets.stream().filter(asset -> {
try { try {
checkAsset(asset); accessControlService.checkPermission(getCurrentUser(), Resource.ASSET, Operation.READ, asset.getId(), asset);
return true; return true;
} catch (ThingsboardException e) { } catch (ThingsboardException e) {
return false; return false;

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

@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.common.data.widget.WidgetsBundle;
@ -74,6 +75,9 @@ import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
import org.thingsboard.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.AccessControlService;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import org.thingsboard.server.service.state.DeviceStateService; import org.thingsboard.server.service.state.DeviceStateService;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
@ -98,6 +102,9 @@ public abstract class BaseController {
@Autowired @Autowired
private ThingsboardErrorResponseHandler errorResponseHandler; private ThingsboardErrorResponseHandler errorResponseHandler;
@Autowired
protected AccessControlService accessControlService;
@Autowired @Autowired
protected TenantService tenantService; protected TenantService tenantService;
@ -252,13 +259,15 @@ public abstract class BaseController {
} }
} }
void checkTenantId(TenantId tenantId) throws ThingsboardException { Tenant checkTenantId(TenantId tenantId, Operation operation) throws ThingsboardException {
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); try {
SecurityUser authUser = getCurrentUser(); validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
if (authUser.getAuthority() != Authority.SYS_ADMIN && Tenant tenant = tenantService.findTenantById(tenantId);
(authUser.getTenantId() == null || !authUser.getTenantId().equals(tenantId))) { checkNotNull(tenant);
throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, accessControlService.checkPermission(getCurrentUser(), Resource.TENANT, operation, tenantId, tenant);
ThingsboardErrorCode.PERMISSION_DENIED); return tenant;
} catch (Exception e) {
throw handleException(e, false);
} }
} }
@ -266,80 +275,61 @@ public abstract class BaseController {
return getCurrentUser().getTenantId(); return getCurrentUser().getTenantId();
} }
Customer checkCustomerId(CustomerId customerId) throws ThingsboardException { Customer checkCustomerId(CustomerId customerId, Operation operation) throws ThingsboardException {
try { try {
SecurityUser authUser = getCurrentUser(); validateId(customerId, "Incorrect customerId " + customerId);
if (authUser.getAuthority() == Authority.SYS_ADMIN || Customer customer = customerService.findCustomerById(getTenantId(), customerId);
(authUser.getAuthority() != Authority.TENANT_ADMIN && checkNotNull(customer);
(authUser.getCustomerId() == null || !authUser.getCustomerId().equals(customerId)))) { accessControlService.checkPermission(getCurrentUser(), Resource.CUSTOMER, operation, customerId, customer);
throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, return customer;
ThingsboardErrorCode.PERMISSION_DENIED);
}
if (customerId != null && !customerId.isNullUid()) {
Customer customer = customerService.findCustomerById(authUser.getTenantId(), customerId);
checkCustomer(customer);
return customer;
} else {
return null;
}
} catch (Exception e) { } catch (Exception e) {
throw handleException(e, false); throw handleException(e, false);
} }
} }
private void checkCustomer(Customer customer) throws ThingsboardException { User checkUserId(UserId userId, Operation operation) throws ThingsboardException {
checkNotNull(customer);
checkTenantId(customer.getTenantId());
}
User checkUserId(UserId userId) throws ThingsboardException {
try { try {
validateId(userId, "Incorrect userId " + userId); validateId(userId, "Incorrect userId " + userId);
User user = userService.findUserById(getCurrentUser().getTenantId(), userId); User user = userService.findUserById(getCurrentUser().getTenantId(), userId);
checkUser(user); checkNotNull(user);
accessControlService.checkPermission(getCurrentUser(), Resource.USER, operation, userId, user);
return user; return user;
} catch (Exception e) { } catch (Exception e) {
throw handleException(e, false); throw handleException(e, false);
} }
} }
private void checkUser(User user) throws ThingsboardException { protected void checkEntityId(EntityId entityId, Operation operation) throws ThingsboardException {
checkNotNull(user);
checkTenantId(user.getTenantId());
if (user.getAuthority() == Authority.CUSTOMER_USER) {
checkCustomerId(user.getCustomerId());
}
}
protected void checkEntityId(EntityId entityId) throws ThingsboardException {
try { try {
checkNotNull(entityId); checkNotNull(entityId);
validateId(entityId.getId(), "Incorrect entityId " + entityId); validateId(entityId.getId(), "Incorrect entityId " + entityId);
SecurityUser authUser = getCurrentUser();
switch (entityId.getEntityType()) { switch (entityId.getEntityType()) {
case DEVICE: case DEVICE:
checkDevice(deviceService.findDeviceById(authUser.getTenantId(), new DeviceId(entityId.getId()))); checkDeviceId(new DeviceId(entityId.getId()), operation);
return; return;
case CUSTOMER: case CUSTOMER:
checkCustomerId(new CustomerId(entityId.getId())); checkCustomerId(new CustomerId(entityId.getId()), operation);
return; return;
case TENANT: case TENANT:
checkTenantId(new TenantId(entityId.getId())); checkTenantId(new TenantId(entityId.getId()), operation);
return; return;
case RULE_CHAIN: case RULE_CHAIN:
checkRuleChain(new RuleChainId(entityId.getId())); checkRuleChain(new RuleChainId(entityId.getId()), operation);
return;
case RULE_NODE:
checkRuleNode(new RuleNodeId(entityId.getId()), operation);
return; return;
case ASSET: case ASSET:
checkAsset(assetService.findAssetById(authUser.getTenantId(), new AssetId(entityId.getId()))); checkAssetId(new AssetId(entityId.getId()), operation);
return; return;
case DASHBOARD: case DASHBOARD:
checkDashboardId(new DashboardId(entityId.getId())); checkDashboardId(new DashboardId(entityId.getId()), operation);
return; return;
case USER: case USER:
checkUserId(new UserId(entityId.getId())); checkUserId(new UserId(entityId.getId()), operation);
return; return;
case ENTITY_VIEW: case ENTITY_VIEW:
checkEntityViewId(new EntityViewId(entityId.getId())); checkEntityViewId(new EntityViewId(entityId.getId()), operation);
return; return;
default: default:
throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType()); throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType());
@ -349,160 +339,114 @@ public abstract class BaseController {
} }
} }
Device checkDeviceId(DeviceId deviceId) throws ThingsboardException { Device checkDeviceId(DeviceId deviceId, Operation operation) throws ThingsboardException {
try { try {
validateId(deviceId, "Incorrect deviceId " + deviceId); validateId(deviceId, "Incorrect deviceId " + deviceId);
Device device = deviceService.findDeviceById(getCurrentUser().getTenantId(), deviceId); Device device = deviceService.findDeviceById(getCurrentUser().getTenantId(), deviceId);
checkDevice(device); checkNotNull(device);
accessControlService.checkPermission(getCurrentUser(), Resource.DEVICE, operation, deviceId, device);
return device; return device;
} catch (Exception e) { } catch (Exception e) {
throw handleException(e, false); throw handleException(e, false);
} }
} }
protected void checkDevice(Device device) throws ThingsboardException { protected EntityView checkEntityViewId(EntityViewId entityViewId, Operation operation) throws ThingsboardException {
checkNotNull(device);
checkTenantId(device.getTenantId());
checkCustomerId(device.getCustomerId());
}
protected EntityView checkEntityViewId(EntityViewId entityViewId) throws ThingsboardException {
try { try {
validateId(entityViewId, "Incorrect entityViewId " + entityViewId); validateId(entityViewId, "Incorrect entityViewId " + entityViewId);
EntityView entityView = entityViewService.findEntityViewById(getCurrentUser().getTenantId(), entityViewId); EntityView entityView = entityViewService.findEntityViewById(getCurrentUser().getTenantId(), entityViewId);
checkEntityView(entityView); checkNotNull(entityView);
accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, operation, entityViewId, entityView);
return entityView; return entityView;
} catch (Exception e) { } catch (Exception e) {
throw handleException(e, false); throw handleException(e, false);
} }
} }
protected void checkEntityView(EntityView entityView) throws ThingsboardException { Asset checkAssetId(AssetId assetId, Operation operation) throws ThingsboardException {
checkNotNull(entityView);
checkTenantId(entityView.getTenantId());
checkCustomerId(entityView.getCustomerId());
}
Asset checkAssetId(AssetId assetId) throws ThingsboardException {
try { try {
validateId(assetId, "Incorrect assetId " + assetId); validateId(assetId, "Incorrect assetId " + assetId);
Asset asset = assetService.findAssetById(getCurrentUser().getTenantId(), assetId); Asset asset = assetService.findAssetById(getCurrentUser().getTenantId(), assetId);
checkAsset(asset); checkNotNull(asset);
accessControlService.checkPermission(getCurrentUser(), Resource.ASSET, operation, assetId, asset);
return asset; return asset;
} catch (Exception e) { } catch (Exception e) {
throw handleException(e, false); throw handleException(e, false);
} }
} }
protected void checkAsset(Asset asset) throws ThingsboardException { Alarm checkAlarmId(AlarmId alarmId, Operation operation) throws ThingsboardException {
checkNotNull(asset);
checkTenantId(asset.getTenantId());
checkCustomerId(asset.getCustomerId());
}
Alarm checkAlarmId(AlarmId alarmId) throws ThingsboardException {
try { try {
validateId(alarmId, "Incorrect alarmId " + alarmId); validateId(alarmId, "Incorrect alarmId " + alarmId);
Alarm alarm = alarmService.findAlarmByIdAsync(getCurrentUser().getTenantId(), alarmId).get(); Alarm alarm = alarmService.findAlarmByIdAsync(getCurrentUser().getTenantId(), alarmId).get();
checkAlarm(alarm); checkNotNull(alarm);
accessControlService.checkPermission(getCurrentUser(), Resource.ALARM, operation, alarmId, alarm);
return alarm; return alarm;
} catch (Exception e) { } catch (Exception e) {
throw handleException(e, false); throw handleException(e, false);
} }
} }
AlarmInfo checkAlarmInfoId(AlarmId alarmId) throws ThingsboardException { AlarmInfo checkAlarmInfoId(AlarmId alarmId, Operation operation) throws ThingsboardException {
try { try {
validateId(alarmId, "Incorrect alarmId " + alarmId); validateId(alarmId, "Incorrect alarmId " + alarmId);
AlarmInfo alarmInfo = alarmService.findAlarmInfoByIdAsync(getCurrentUser().getTenantId(), alarmId).get(); AlarmInfo alarmInfo = alarmService.findAlarmInfoByIdAsync(getCurrentUser().getTenantId(), alarmId).get();
checkAlarm(alarmInfo); checkNotNull(alarmInfo);
accessControlService.checkPermission(getCurrentUser(), Resource.ALARM, operation, alarmId, alarmInfo);
return alarmInfo; return alarmInfo;
} catch (Exception e) { } catch (Exception e) {
throw handleException(e, false); throw handleException(e, false);
} }
} }
protected void checkAlarm(Alarm alarm) throws ThingsboardException { WidgetsBundle checkWidgetsBundleId(WidgetsBundleId widgetsBundleId, Operation operation) throws ThingsboardException {
checkNotNull(alarm);
checkTenantId(alarm.getTenantId());
}
WidgetsBundle checkWidgetsBundleId(WidgetsBundleId widgetsBundleId, boolean modify) throws ThingsboardException {
try { try {
validateId(widgetsBundleId, "Incorrect widgetsBundleId " + widgetsBundleId); validateId(widgetsBundleId, "Incorrect widgetsBundleId " + widgetsBundleId);
WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleById(getCurrentUser().getTenantId(), widgetsBundleId); WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleById(getCurrentUser().getTenantId(), widgetsBundleId);
checkWidgetsBundle(widgetsBundle, modify); checkNotNull(widgetsBundle);
accessControlService.checkPermission(getCurrentUser(), Resource.WIDGETS_BUNDLE, operation, widgetsBundleId, widgetsBundle);
return widgetsBundle; return widgetsBundle;
} catch (Exception e) { } catch (Exception e) {
throw handleException(e, false); throw handleException(e, false);
} }
} }
private void checkWidgetsBundle(WidgetsBundle widgetsBundle, boolean modify) throws ThingsboardException { WidgetType checkWidgetTypeId(WidgetTypeId widgetTypeId, Operation operation) throws ThingsboardException {
checkNotNull(widgetsBundle);
if (widgetsBundle.getTenantId() != null && !widgetsBundle.getTenantId().getId().equals(ModelConstants.NULL_UUID)) {
checkTenantId(widgetsBundle.getTenantId());
} else if (modify && getCurrentUser().getAuthority() != Authority.SYS_ADMIN) {
throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION,
ThingsboardErrorCode.PERMISSION_DENIED);
}
}
WidgetType checkWidgetTypeId(WidgetTypeId widgetTypeId, boolean modify) throws ThingsboardException {
try { try {
validateId(widgetTypeId, "Incorrect widgetTypeId " + widgetTypeId); validateId(widgetTypeId, "Incorrect widgetTypeId " + widgetTypeId);
WidgetType widgetType = widgetTypeService.findWidgetTypeById(getCurrentUser().getTenantId(), widgetTypeId); WidgetType widgetType = widgetTypeService.findWidgetTypeById(getCurrentUser().getTenantId(), widgetTypeId);
checkWidgetType(widgetType, modify); checkNotNull(widgetType);
accessControlService.checkPermission(getCurrentUser(), Resource.WIDGET_TYPE, operation, widgetTypeId, widgetType);
return widgetType; return widgetType;
} catch (Exception e) { } catch (Exception e) {
throw handleException(e, false); throw handleException(e, false);
} }
} }
void checkWidgetType(WidgetType widgetType, boolean modify) throws ThingsboardException { Dashboard checkDashboardId(DashboardId dashboardId, Operation operation) throws ThingsboardException {
checkNotNull(widgetType);
if (widgetType.getTenantId() != null && !widgetType.getTenantId().getId().equals(ModelConstants.NULL_UUID)) {
checkTenantId(widgetType.getTenantId());
} else if (modify && getCurrentUser().getAuthority() != Authority.SYS_ADMIN) {
throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION,
ThingsboardErrorCode.PERMISSION_DENIED);
}
}
Dashboard checkDashboardId(DashboardId dashboardId) throws ThingsboardException {
try { try {
validateId(dashboardId, "Incorrect dashboardId " + dashboardId); validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
Dashboard dashboard = dashboardService.findDashboardById(getCurrentUser().getTenantId(), dashboardId); Dashboard dashboard = dashboardService.findDashboardById(getCurrentUser().getTenantId(), dashboardId);
checkDashboard(dashboard); checkNotNull(dashboard);
accessControlService.checkPermission(getCurrentUser(), Resource.DASHBOARD, operation, dashboardId, dashboard);
return dashboard; return dashboard;
} catch (Exception e) { } catch (Exception e) {
throw handleException(e, false); throw handleException(e, false);
} }
} }
DashboardInfo checkDashboardInfoId(DashboardId dashboardId) throws ThingsboardException { DashboardInfo checkDashboardInfoId(DashboardId dashboardId, Operation operation) throws ThingsboardException {
try { try {
validateId(dashboardId, "Incorrect dashboardId " + dashboardId); validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
DashboardInfo dashboardInfo = dashboardService.findDashboardInfoById(getCurrentUser().getTenantId(), dashboardId); DashboardInfo dashboardInfo = dashboardService.findDashboardInfoById(getCurrentUser().getTenantId(), dashboardId);
checkDashboard(dashboardInfo); checkNotNull(dashboardInfo);
accessControlService.checkPermission(getCurrentUser(), Resource.DASHBOARD, operation, dashboardId, dashboardInfo);
return dashboardInfo; return dashboardInfo;
} catch (Exception e) { } catch (Exception e) {
throw handleException(e, false); throw handleException(e, false);
} }
} }
private void checkDashboard(DashboardInfo dashboard) throws ThingsboardException {
checkNotNull(dashboard);
checkTenantId(dashboard.getTenantId());
SecurityUser authUser = getCurrentUser();
if (authUser.getAuthority() == Authority.CUSTOMER_USER) {
if (!dashboard.isAssignedToCustomer(authUser.getCustomerId())) {
throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION,
ThingsboardErrorCode.PERMISSION_DENIED);
}
}
}
ComponentDescriptor checkComponentDescriptorByClazz(String clazz) throws ThingsboardException { ComponentDescriptor checkComponentDescriptorByClazz(String clazz) throws ThingsboardException {
try { try {
log.debug("[{}] Lookup component descriptor", clazz); log.debug("[{}] Lookup component descriptor", clazz);
@ -530,24 +474,22 @@ public abstract class BaseController {
} }
} }
protected RuleChain checkRuleChain(RuleChainId ruleChainId) throws ThingsboardException { protected RuleChain checkRuleChain(RuleChainId ruleChainId, Operation operation) throws ThingsboardException {
checkNotNull(ruleChainId); validateId(ruleChainId, "Incorrect ruleChainId " + ruleChainId);
return checkRuleChain(ruleChainService.findRuleChainById(getCurrentUser().getTenantId(), ruleChainId)); RuleChain ruleChain = ruleChainService.findRuleChainById(getCurrentUser().getTenantId(), ruleChainId);
}
protected RuleChain checkRuleChain(RuleChain ruleChain) throws ThingsboardException {
checkNotNull(ruleChain); checkNotNull(ruleChain);
SecurityUser authUser = getCurrentUser(); accessControlService.checkPermission(getCurrentUser(), Resource.RULE_CHAIN, operation, ruleChainId, ruleChain);
TenantId tenantId = ruleChain.getTenantId();
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
if (authUser.getAuthority() != Authority.TENANT_ADMIN ||
!authUser.getTenantId().equals(tenantId)) {
throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION,
ThingsboardErrorCode.PERMISSION_DENIED);
}
return ruleChain; return ruleChain;
} }
protected RuleNode checkRuleNode(RuleNodeId ruleNodeId, Operation operation) throws ThingsboardException {
validateId(ruleNodeId, "Incorrect ruleNodeId " + ruleNodeId);
RuleNode ruleNode = ruleChainService.findRuleNodeById(getTenantId(), ruleNodeId);
checkNotNull(ruleNode);
checkRuleChain(ruleNode.getRuleChainId(), operation);
return ruleNode;
}
protected String constructBaseUrl(HttpServletRequest request) { protected String constructBaseUrl(HttpServletRequest request) {
String scheme = request.getScheme(); String scheme = request.getScheme();

14
application/src/main/java/org/thingsboard/server/controller/CustomerController.java

@ -36,6 +36,8 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
@RestController @RestController
@RequestMapping("/api") @RequestMapping("/api")
@ -51,7 +53,7 @@ public class CustomerController extends BaseController {
checkParameter(CUSTOMER_ID, strCustomerId); checkParameter(CUSTOMER_ID, strCustomerId);
try { try {
CustomerId customerId = new CustomerId(toUUID(strCustomerId)); CustomerId customerId = new CustomerId(toUUID(strCustomerId));
return checkCustomerId(customerId); return checkCustomerId(customerId, Operation.READ);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -64,7 +66,7 @@ public class CustomerController extends BaseController {
checkParameter(CUSTOMER_ID, strCustomerId); checkParameter(CUSTOMER_ID, strCustomerId);
try { try {
CustomerId customerId = new CustomerId(toUUID(strCustomerId)); CustomerId customerId = new CustomerId(toUUID(strCustomerId));
Customer customer = checkCustomerId(customerId); Customer customer = checkCustomerId(customerId, Operation.READ);
ObjectMapper objectMapper = new ObjectMapper(); ObjectMapper objectMapper = new ObjectMapper();
ObjectNode infoObject = objectMapper.createObjectNode(); ObjectNode infoObject = objectMapper.createObjectNode();
infoObject.put("title", customer.getTitle()); infoObject.put("title", customer.getTitle());
@ -82,7 +84,7 @@ public class CustomerController extends BaseController {
checkParameter(CUSTOMER_ID, strCustomerId); checkParameter(CUSTOMER_ID, strCustomerId);
try { try {
CustomerId customerId = new CustomerId(toUUID(strCustomerId)); CustomerId customerId = new CustomerId(toUUID(strCustomerId));
Customer customer = checkCustomerId(customerId); Customer customer = checkCustomerId(customerId, Operation.READ);
return customer.getTitle(); return customer.getTitle();
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
@ -95,6 +97,10 @@ public class CustomerController extends BaseController {
public Customer saveCustomer(@RequestBody Customer customer) throws ThingsboardException { public Customer saveCustomer(@RequestBody Customer customer) throws ThingsboardException {
try { try {
customer.setTenantId(getCurrentUser().getTenantId()); customer.setTenantId(getCurrentUser().getTenantId());
Operation operation = customer.getId() == null ? Operation.CREATE : Operation.WRITE;
accessControlService.checkPermission(getCurrentUser(), Resource.CUSTOMER, operation, customer.getId(), customer);
Customer savedCustomer = checkNotNull(customerService.saveCustomer(customer)); Customer savedCustomer = checkNotNull(customerService.saveCustomer(customer));
logEntityAction(savedCustomer.getId(), savedCustomer, logEntityAction(savedCustomer.getId(), savedCustomer,
@ -118,7 +124,7 @@ public class CustomerController extends BaseController {
checkParameter(CUSTOMER_ID, strCustomerId); checkParameter(CUSTOMER_ID, strCustomerId);
try { try {
CustomerId customerId = new CustomerId(toUUID(strCustomerId)); CustomerId customerId = new CustomerId(toUUID(strCustomerId));
Customer customer = checkCustomerId(customerId); Customer customer = checkCustomerId(customerId, Operation.DELETE);
customerService.deleteCustomer(getTenantId(), customerId); customerService.deleteCustomer(getTenantId(), customerId);
logEntityAction(customerId, customer, logEntityAction(customerId, customer,

36
application/src/main/java/org/thingsboard/server/controller/DashboardController.java

@ -41,6 +41,8 @@ import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -76,7 +78,7 @@ public class DashboardController extends BaseController {
checkParameter(DASHBOARD_ID, strDashboardId); checkParameter(DASHBOARD_ID, strDashboardId);
try { try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
return checkDashboardInfoId(dashboardId); return checkDashboardInfoId(dashboardId, Operation.READ);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -89,7 +91,7 @@ public class DashboardController extends BaseController {
checkParameter(DASHBOARD_ID, strDashboardId); checkParameter(DASHBOARD_ID, strDashboardId);
try { try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
return checkDashboardId(dashboardId); return checkDashboardId(dashboardId, Operation.READ);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -101,6 +103,12 @@ public class DashboardController extends BaseController {
public Dashboard saveDashboard(@RequestBody Dashboard dashboard) throws ThingsboardException { public Dashboard saveDashboard(@RequestBody Dashboard dashboard) throws ThingsboardException {
try { try {
dashboard.setTenantId(getCurrentUser().getTenantId()); dashboard.setTenantId(getCurrentUser().getTenantId());
Operation operation = dashboard.getId() == null ? Operation.CREATE : Operation.WRITE;
accessControlService.checkPermission(getCurrentUser(), Resource.DASHBOARD, operation,
dashboard.getId(), dashboard);
Dashboard savedDashboard = checkNotNull(dashboardService.saveDashboard(dashboard)); Dashboard savedDashboard = checkNotNull(dashboardService.saveDashboard(dashboard));
logEntityAction(savedDashboard.getId(), savedDashboard, logEntityAction(savedDashboard.getId(), savedDashboard,
@ -123,7 +131,7 @@ public class DashboardController extends BaseController {
checkParameter(DASHBOARD_ID, strDashboardId); checkParameter(DASHBOARD_ID, strDashboardId);
try { try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId); Dashboard dashboard = checkDashboardId(dashboardId, Operation.DELETE);
dashboardService.deleteDashboard(getCurrentUser().getTenantId(), dashboardId); dashboardService.deleteDashboard(getCurrentUser().getTenantId(), dashboardId);
logEntityAction(dashboardId, dashboard, logEntityAction(dashboardId, dashboard,
@ -150,10 +158,10 @@ public class DashboardController extends BaseController {
checkParameter(DASHBOARD_ID, strDashboardId); checkParameter(DASHBOARD_ID, strDashboardId);
try { try {
CustomerId customerId = new CustomerId(toUUID(strCustomerId)); CustomerId customerId = new CustomerId(toUUID(strCustomerId));
Customer customer = checkCustomerId(customerId); Customer customer = checkCustomerId(customerId, Operation.READ);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
checkDashboardId(dashboardId); checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(getCurrentUser().getTenantId(), dashboardId, customerId)); Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(getCurrentUser().getTenantId(), dashboardId, customerId));
@ -182,9 +190,9 @@ public class DashboardController extends BaseController {
checkParameter(DASHBOARD_ID, strDashboardId); checkParameter(DASHBOARD_ID, strDashboardId);
try { try {
CustomerId customerId = new CustomerId(toUUID(strCustomerId)); CustomerId customerId = new CustomerId(toUUID(strCustomerId));
Customer customer = checkCustomerId(customerId); Customer customer = checkCustomerId(customerId, Operation.READ);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId); Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_CUSTOMER);
Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(getCurrentUser().getTenantId(), dashboardId, customerId)); Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(getCurrentUser().getTenantId(), dashboardId, customerId));
@ -211,7 +219,7 @@ public class DashboardController extends BaseController {
checkParameter(DASHBOARD_ID, strDashboardId); checkParameter(DASHBOARD_ID, strDashboardId);
try { try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId); Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
Set<CustomerId> customerIds = new HashSet<>(); Set<CustomerId> customerIds = new HashSet<>();
if (strCustomerIds != null) { if (strCustomerIds != null) {
@ -276,7 +284,7 @@ public class DashboardController extends BaseController {
checkParameter(DASHBOARD_ID, strDashboardId); checkParameter(DASHBOARD_ID, strDashboardId);
try { try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId); Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
Set<CustomerId> customerIds = new HashSet<>(); Set<CustomerId> customerIds = new HashSet<>();
if (strCustomerIds != null) { if (strCustomerIds != null) {
@ -319,7 +327,7 @@ public class DashboardController extends BaseController {
checkParameter(DASHBOARD_ID, strDashboardId); checkParameter(DASHBOARD_ID, strDashboardId);
try { try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId); Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_CUSTOMER);
Set<CustomerId> customerIds = new HashSet<>(); Set<CustomerId> customerIds = new HashSet<>();
if (strCustomerIds != null) { if (strCustomerIds != null) {
@ -362,7 +370,7 @@ public class DashboardController extends BaseController {
checkParameter(DASHBOARD_ID, strDashboardId); checkParameter(DASHBOARD_ID, strDashboardId);
try { try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId); Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
Customer publicCustomer = customerService.findOrCreatePublicCustomer(dashboard.getTenantId()); Customer publicCustomer = customerService.findOrCreatePublicCustomer(dashboard.getTenantId());
Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(getCurrentUser().getTenantId(), dashboardId, publicCustomer.getId())); Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(getCurrentUser().getTenantId(), dashboardId, publicCustomer.getId()));
@ -388,7 +396,7 @@ public class DashboardController extends BaseController {
checkParameter(DASHBOARD_ID, strDashboardId); checkParameter(DASHBOARD_ID, strDashboardId);
try { try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId); Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_CUSTOMER);
Customer publicCustomer = customerService.findOrCreatePublicCustomer(dashboard.getTenantId()); Customer publicCustomer = customerService.findOrCreatePublicCustomer(dashboard.getTenantId());
Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(getCurrentUser().getTenantId(), dashboardId, publicCustomer.getId())); Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(getCurrentUser().getTenantId(), dashboardId, publicCustomer.getId()));
@ -419,7 +427,7 @@ public class DashboardController extends BaseController {
@RequestParam(required = false) String textOffset) throws ThingsboardException { @RequestParam(required = false) String textOffset) throws ThingsboardException {
try { try {
TenantId tenantId = new TenantId(toUUID(strTenantId)); TenantId tenantId = new TenantId(toUUID(strTenantId));
checkTenantId(tenantId); checkTenantId(tenantId, Operation.READ);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(dashboardService.findDashboardsByTenantId(tenantId, pageLink)); return checkNotNull(dashboardService.findDashboardsByTenantId(tenantId, pageLink));
} catch (Exception e) { } catch (Exception e) {
@ -458,7 +466,7 @@ public class DashboardController extends BaseController {
try { try {
TenantId tenantId = getCurrentUser().getTenantId(); TenantId tenantId = getCurrentUser().getTenantId();
CustomerId customerId = new CustomerId(toUUID(strCustomerId)); CustomerId customerId = new CustomerId(toUUID(strCustomerId));
checkCustomerId(customerId); checkCustomerId(customerId, Operation.READ);
TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
return checkNotNull(dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink).get()); return checkNotNull(dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink).get());
} catch (Exception e) { } catch (Exception e) {

41
application/src/main/java/org/thingsboard/server/controller/DeviceController.java

@ -44,6 +44,8 @@ import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -62,7 +64,7 @@ public class DeviceController extends BaseController {
checkParameter(DEVICE_ID, strDeviceId); checkParameter(DEVICE_ID, strDeviceId);
try { try {
DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
return checkDeviceId(deviceId); return checkDeviceId(deviceId, Operation.READ);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -74,15 +76,12 @@ public class DeviceController extends BaseController {
public Device saveDevice(@RequestBody Device device) throws ThingsboardException { public Device saveDevice(@RequestBody Device device) throws ThingsboardException {
try { try {
device.setTenantId(getCurrentUser().getTenantId()); device.setTenantId(getCurrentUser().getTenantId());
if (getCurrentUser().getAuthority() == Authority.CUSTOMER_USER) {
if (device.getId() == null || device.getId().isNullUid() || Operation operation = device.getId() == null ? Operation.CREATE : Operation.WRITE;
device.getCustomerId() == null || device.getCustomerId().isNullUid()) {
throw new ThingsboardException("You don't have permission to perform this operation!", accessControlService.checkPermission(getCurrentUser(), Resource.DEVICE, operation,
ThingsboardErrorCode.PERMISSION_DENIED); device.getId(), device);
} else {
checkCustomerId(device.getCustomerId());
}
}
Device savedDevice = checkNotNull(deviceService.saveDevice(device)); Device savedDevice = checkNotNull(deviceService.saveDevice(device));
actorService actorService
@ -116,7 +115,7 @@ public class DeviceController extends BaseController {
checkParameter(DEVICE_ID, strDeviceId); checkParameter(DEVICE_ID, strDeviceId);
try { try {
DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
Device device = checkDeviceId(deviceId); Device device = checkDeviceId(deviceId, Operation.DELETE);
deviceService.deleteDevice(getCurrentUser().getTenantId(), deviceId); deviceService.deleteDevice(getCurrentUser().getTenantId(), deviceId);
logEntityAction(deviceId, device, logEntityAction(deviceId, device,
@ -142,10 +141,10 @@ public class DeviceController extends BaseController {
checkParameter(DEVICE_ID, strDeviceId); checkParameter(DEVICE_ID, strDeviceId);
try { try {
CustomerId customerId = new CustomerId(toUUID(strCustomerId)); CustomerId customerId = new CustomerId(toUUID(strCustomerId));
Customer customer = checkCustomerId(customerId); Customer customer = checkCustomerId(customerId, Operation.READ);
DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
checkDeviceId(deviceId); checkDeviceId(deviceId, Operation.ASSIGN_TO_CUSTOMER);
Device savedDevice = checkNotNull(deviceService.assignDeviceToCustomer(getCurrentUser().getTenantId(), deviceId, customerId)); Device savedDevice = checkNotNull(deviceService.assignDeviceToCustomer(getCurrentUser().getTenantId(), deviceId, customerId));
@ -169,11 +168,11 @@ public class DeviceController extends BaseController {
checkParameter(DEVICE_ID, strDeviceId); checkParameter(DEVICE_ID, strDeviceId);
try { try {
DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
Device device = checkDeviceId(deviceId); Device device = checkDeviceId(deviceId, Operation.UNASSIGN_FROM_CUSTOMER);
if (device.getCustomerId() == null || device.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { if (device.getCustomerId() == null || device.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
throw new IncorrectParameterException("Device isn't assigned to any customer!"); throw new IncorrectParameterException("Device isn't assigned to any customer!");
} }
Customer customer = checkCustomerId(device.getCustomerId()); Customer customer = checkCustomerId(device.getCustomerId(), Operation.READ);
Device savedDevice = checkNotNull(deviceService.unassignDeviceFromCustomer(getCurrentUser().getTenantId(), deviceId)); Device savedDevice = checkNotNull(deviceService.unassignDeviceFromCustomer(getCurrentUser().getTenantId(), deviceId));
@ -197,7 +196,7 @@ public class DeviceController extends BaseController {
checkParameter(DEVICE_ID, strDeviceId); checkParameter(DEVICE_ID, strDeviceId);
try { try {
DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
Device device = checkDeviceId(deviceId); Device device = checkDeviceId(deviceId, Operation.ASSIGN_TO_CUSTOMER);
Customer publicCustomer = customerService.findOrCreatePublicCustomer(device.getTenantId()); Customer publicCustomer = customerService.findOrCreatePublicCustomer(device.getTenantId());
Device savedDevice = checkNotNull(deviceService.assignDeviceToCustomer(getCurrentUser().getTenantId(), deviceId, publicCustomer.getId())); Device savedDevice = checkNotNull(deviceService.assignDeviceToCustomer(getCurrentUser().getTenantId(), deviceId, publicCustomer.getId()));
@ -221,7 +220,7 @@ public class DeviceController extends BaseController {
checkParameter(DEVICE_ID, strDeviceId); checkParameter(DEVICE_ID, strDeviceId);
try { try {
DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
Device device = checkDeviceId(deviceId); Device device = checkDeviceId(deviceId, Operation.READ_CREDENTIALS);
DeviceCredentials deviceCredentials = checkNotNull(deviceCredentialsService.findDeviceCredentialsByDeviceId(getCurrentUser().getTenantId(), deviceId)); DeviceCredentials deviceCredentials = checkNotNull(deviceCredentialsService.findDeviceCredentialsByDeviceId(getCurrentUser().getTenantId(), deviceId));
logEntityAction(deviceId, device, logEntityAction(deviceId, device,
device.getCustomerId(), device.getCustomerId(),
@ -241,7 +240,7 @@ public class DeviceController extends BaseController {
public DeviceCredentials saveDeviceCredentials(@RequestBody DeviceCredentials deviceCredentials) throws ThingsboardException { public DeviceCredentials saveDeviceCredentials(@RequestBody DeviceCredentials deviceCredentials) throws ThingsboardException {
checkNotNull(deviceCredentials); checkNotNull(deviceCredentials);
try { try {
Device device = checkDeviceId(deviceCredentials.getDeviceId()); Device device = checkDeviceId(deviceCredentials.getDeviceId(), Operation.WRITE_CREDENTIALS);
DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(getCurrentUser().getTenantId(), deviceCredentials)); DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(getCurrentUser().getTenantId(), deviceCredentials));
actorService.onCredentialsUpdate(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId()); actorService.onCredentialsUpdate(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId());
logEntityAction(device.getId(), device, logEntityAction(device.getId(), device,
@ -305,7 +304,7 @@ public class DeviceController extends BaseController {
try { try {
TenantId tenantId = getCurrentUser().getTenantId(); TenantId tenantId = getCurrentUser().getTenantId();
CustomerId customerId = new CustomerId(toUUID(strCustomerId)); CustomerId customerId = new CustomerId(toUUID(strCustomerId));
checkCustomerId(customerId); checkCustomerId(customerId, Operation.READ);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
if (type != null && type.trim().length() > 0) { if (type != null && type.trim().length() > 0) {
return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink));
@ -350,12 +349,12 @@ public class DeviceController extends BaseController {
checkNotNull(query); checkNotNull(query);
checkNotNull(query.getParameters()); checkNotNull(query.getParameters());
checkNotNull(query.getDeviceTypes()); checkNotNull(query.getDeviceTypes());
checkEntityId(query.getParameters().getEntityId()); checkEntityId(query.getParameters().getEntityId(), Operation.READ);
try { try {
List<Device> devices = checkNotNull(deviceService.findDevicesByQuery(getCurrentUser().getTenantId(), query).get()); List<Device> devices = checkNotNull(deviceService.findDevicesByQuery(getCurrentUser().getTenantId(), query).get());
devices = devices.stream().filter(device -> { devices = devices.stream().filter(device -> {
try { try {
checkDevice(device); accessControlService.checkPermission(getCurrentUser(), Resource.DEVICE, Operation.READ, device.getId(), device);
return true; return true;
} catch (ThingsboardException e) { } catch (ThingsboardException e) {
return false; return false;

31
application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java

@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntityRelationInfo; import org.thingsboard.server.common.data.relation.EntityRelationInfo;
import org.thingsboard.server.common.data.relation.EntityRelationsQuery; import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.service.security.permission.Operation;
import java.util.List; import java.util.List;
@ -55,8 +56,8 @@ public class EntityRelationController extends BaseController {
public void saveRelation(@RequestBody EntityRelation relation) throws ThingsboardException { public void saveRelation(@RequestBody EntityRelation relation) throws ThingsboardException {
try { try {
checkNotNull(relation); checkNotNull(relation);
checkEntityId(relation.getFrom()); checkEntityId(relation.getFrom(), Operation.WRITE);
checkEntityId(relation.getTo()); checkEntityId(relation.getTo(), Operation.WRITE);
if (relation.getTypeGroup() == null) { if (relation.getTypeGroup() == null) {
relation.setTypeGroup(RelationTypeGroup.COMMON); relation.setTypeGroup(RelationTypeGroup.COMMON);
} }
@ -89,8 +90,8 @@ public class EntityRelationController extends BaseController {
checkParameter(TO_TYPE, strToType); checkParameter(TO_TYPE, strToType);
EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId);
EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId); EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId);
checkEntityId(fromId); checkEntityId(fromId, Operation.WRITE);
checkEntityId(toId); checkEntityId(toId, Operation.WRITE);
RelationTypeGroup relationTypeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); RelationTypeGroup relationTypeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
EntityRelation relation = new EntityRelation(fromId, toId, strRelationType, relationTypeGroup); EntityRelation relation = new EntityRelation(fromId, toId, strRelationType, relationTypeGroup);
try { try {
@ -119,7 +120,7 @@ public class EntityRelationController extends BaseController {
checkParameter("entityId", strId); checkParameter("entityId", strId);
checkParameter("entityType", strType); checkParameter("entityType", strType);
EntityId entityId = EntityIdFactory.getByTypeAndId(strType, strId); EntityId entityId = EntityIdFactory.getByTypeAndId(strType, strId);
checkEntityId(entityId); checkEntityId(entityId, Operation.WRITE);
try { try {
relationService.deleteEntityRelations(getTenantId(), entityId); relationService.deleteEntityRelations(getTenantId(), entityId);
logEntityAction(entityId, null, getCurrentUser().getCustomerId(), ActionType.RELATIONS_DELETED, null); logEntityAction(entityId, null, getCurrentUser().getCustomerId(), ActionType.RELATIONS_DELETED, null);
@ -145,8 +146,8 @@ public class EntityRelationController extends BaseController {
checkParameter(TO_TYPE, strToType); checkParameter(TO_TYPE, strToType);
EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId);
EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId); EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId);
checkEntityId(fromId); checkEntityId(fromId, Operation.READ);
checkEntityId(toId); checkEntityId(toId, Operation.READ);
RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
return checkNotNull(relationService.getRelation(getTenantId(), fromId, toId, strRelationType, typeGroup)); return checkNotNull(relationService.getRelation(getTenantId(), fromId, toId, strRelationType, typeGroup));
} catch (Exception e) { } catch (Exception e) {
@ -163,7 +164,7 @@ public class EntityRelationController extends BaseController {
checkParameter(FROM_ID, strFromId); checkParameter(FROM_ID, strFromId);
checkParameter(FROM_TYPE, strFromType); checkParameter(FROM_TYPE, strFromType);
EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId);
checkEntityId(entityId); checkEntityId(entityId, Operation.READ);
RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
try { try {
return checkNotNull(relationService.findByFrom(getTenantId(), entityId, typeGroup)); return checkNotNull(relationService.findByFrom(getTenantId(), entityId, typeGroup));
@ -181,7 +182,7 @@ public class EntityRelationController extends BaseController {
checkParameter(FROM_ID, strFromId); checkParameter(FROM_ID, strFromId);
checkParameter(FROM_TYPE, strFromType); checkParameter(FROM_TYPE, strFromType);
EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId);
checkEntityId(entityId); checkEntityId(entityId, Operation.READ);
RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
try { try {
return checkNotNull(relationService.findInfoByFrom(getTenantId(), entityId, typeGroup).get()); return checkNotNull(relationService.findInfoByFrom(getTenantId(), entityId, typeGroup).get());
@ -201,7 +202,7 @@ public class EntityRelationController extends BaseController {
checkParameter(FROM_TYPE, strFromType); checkParameter(FROM_TYPE, strFromType);
checkParameter(RELATION_TYPE, strRelationType); checkParameter(RELATION_TYPE, strRelationType);
EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId);
checkEntityId(entityId); checkEntityId(entityId, Operation.READ);
RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
try { try {
return checkNotNull(relationService.findByFromAndType(getTenantId(), entityId, strRelationType, typeGroup)); return checkNotNull(relationService.findByFromAndType(getTenantId(), entityId, strRelationType, typeGroup));
@ -219,7 +220,7 @@ public class EntityRelationController extends BaseController {
checkParameter(TO_ID, strToId); checkParameter(TO_ID, strToId);
checkParameter(TO_TYPE, strToType); checkParameter(TO_TYPE, strToType);
EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId); EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId);
checkEntityId(entityId); checkEntityId(entityId, Operation.READ);
RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
try { try {
return checkNotNull(relationService.findByTo(getTenantId(), entityId, typeGroup)); return checkNotNull(relationService.findByTo(getTenantId(), entityId, typeGroup));
@ -237,7 +238,7 @@ public class EntityRelationController extends BaseController {
checkParameter(TO_ID, strToId); checkParameter(TO_ID, strToId);
checkParameter(TO_TYPE, strToType); checkParameter(TO_TYPE, strToType);
EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId); EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId);
checkEntityId(entityId); checkEntityId(entityId, Operation.READ);
RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
try { try {
return checkNotNull(relationService.findInfoByTo(getTenantId(), entityId, typeGroup).get()); return checkNotNull(relationService.findInfoByTo(getTenantId(), entityId, typeGroup).get());
@ -257,7 +258,7 @@ public class EntityRelationController extends BaseController {
checkParameter(TO_TYPE, strToType); checkParameter(TO_TYPE, strToType);
checkParameter(RELATION_TYPE, strRelationType); checkParameter(RELATION_TYPE, strRelationType);
EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId); EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId);
checkEntityId(entityId); checkEntityId(entityId, Operation.READ);
RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
try { try {
return checkNotNull(relationService.findByToAndType(getTenantId(), entityId, strRelationType, typeGroup)); return checkNotNull(relationService.findByToAndType(getTenantId(), entityId, strRelationType, typeGroup));
@ -273,7 +274,7 @@ public class EntityRelationController extends BaseController {
checkNotNull(query); checkNotNull(query);
checkNotNull(query.getParameters()); checkNotNull(query.getParameters());
checkNotNull(query.getFilters()); checkNotNull(query.getFilters());
checkEntityId(query.getParameters().getEntityId()); checkEntityId(query.getParameters().getEntityId(), Operation.READ);
try { try {
return checkNotNull(relationService.findByQuery(getTenantId(), query).get()); return checkNotNull(relationService.findByQuery(getTenantId(), query).get());
} catch (Exception e) { } catch (Exception e) {
@ -288,7 +289,7 @@ public class EntityRelationController extends BaseController {
checkNotNull(query); checkNotNull(query);
checkNotNull(query.getParameters()); checkNotNull(query.getParameters());
checkNotNull(query.getFilters()); checkNotNull(query.getFilters());
checkEntityId(query.getParameters().getEntityId()); checkEntityId(query.getParameters().getEntityId(), Operation.READ);
try { try {
return checkNotNull(relationService.findInfoByQuery(getTenantId(), query).get()); return checkNotNull(relationService.findInfoByQuery(getTenantId(), query).get());
} catch (Exception e) { } catch (Exception e) {

28
application/src/main/java/org/thingsboard/server/controller/EntityViewController.java

@ -48,6 +48,8 @@ import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
@ -74,7 +76,7 @@ public class EntityViewController extends BaseController {
public EntityView getEntityViewById(@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { public EntityView getEntityViewById(@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
checkParameter(ENTITY_VIEW_ID, strEntityViewId); checkParameter(ENTITY_VIEW_ID, strEntityViewId);
try { try {
return checkEntityViewId(new EntityViewId(toUUID(strEntityViewId))); return checkEntityViewId(new EntityViewId(toUUID(strEntityViewId)), Operation.READ);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -86,6 +88,12 @@ public class EntityViewController extends BaseController {
public EntityView saveEntityView(@RequestBody EntityView entityView) throws ThingsboardException { public EntityView saveEntityView(@RequestBody EntityView entityView) throws ThingsboardException {
try { try {
entityView.setTenantId(getCurrentUser().getTenantId()); entityView.setTenantId(getCurrentUser().getTenantId());
Operation operation = entityView.getId() == null ? Operation.CREATE : Operation.WRITE;
accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, operation,
entityView.getId(), entityView);
EntityView savedEntityView = checkNotNull(entityViewService.saveEntityView(entityView)); EntityView savedEntityView = checkNotNull(entityViewService.saveEntityView(entityView));
List<ListenableFuture<List<Void>>> futures = new ArrayList<>(); List<ListenableFuture<List<Void>>> futures = new ArrayList<>();
if (savedEntityView.getKeys() != null && savedEntityView.getKeys().getAttributes() != null) { if (savedEntityView.getKeys() != null && savedEntityView.getKeys().getAttributes() != null) {
@ -168,7 +176,7 @@ public class EntityViewController extends BaseController {
checkParameter(ENTITY_VIEW_ID, strEntityViewId); checkParameter(ENTITY_VIEW_ID, strEntityViewId);
try { try {
EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId)); EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
EntityView entityView = checkEntityViewId(entityViewId); EntityView entityView = checkEntityViewId(entityViewId, Operation.DELETE);
entityViewService.deleteEntityView(getTenantId(), entityViewId); entityViewService.deleteEntityView(getTenantId(), entityViewId);
logEntityAction(entityViewId, entityView, entityView.getCustomerId(), logEntityAction(entityViewId, entityView, entityView.getCustomerId(),
ActionType.DELETED, null, strEntityViewId); ActionType.DELETED, null, strEntityViewId);
@ -203,10 +211,10 @@ public class EntityViewController extends BaseController {
checkParameter(ENTITY_VIEW_ID, strEntityViewId); checkParameter(ENTITY_VIEW_ID, strEntityViewId);
try { try {
CustomerId customerId = new CustomerId(toUUID(strCustomerId)); CustomerId customerId = new CustomerId(toUUID(strCustomerId));
Customer customer = checkCustomerId(customerId); Customer customer = checkCustomerId(customerId, Operation.READ);
EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId)); EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
checkEntityViewId(entityViewId); checkEntityViewId(entityViewId, Operation.ASSIGN_TO_CUSTOMER);
EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToCustomer(getTenantId(), entityViewId, customerId)); EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToCustomer(getTenantId(), entityViewId, customerId));
logEntityAction(entityViewId, savedEntityView, logEntityAction(entityViewId, savedEntityView,
@ -228,11 +236,11 @@ public class EntityViewController extends BaseController {
checkParameter(ENTITY_VIEW_ID, strEntityViewId); checkParameter(ENTITY_VIEW_ID, strEntityViewId);
try { try {
EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId)); EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
EntityView entityView = checkEntityViewId(entityViewId); EntityView entityView = checkEntityViewId(entityViewId, Operation.UNASSIGN_FROM_CUSTOMER);
if (entityView.getCustomerId() == null || entityView.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { if (entityView.getCustomerId() == null || entityView.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
throw new IncorrectParameterException("Entity View isn't assigned to any customer!"); throw new IncorrectParameterException("Entity View isn't assigned to any customer!");
} }
Customer customer = checkCustomerId(entityView.getCustomerId()); Customer customer = checkCustomerId(entityView.getCustomerId(), Operation.READ);
EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromCustomer(getTenantId(), entityViewId)); EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromCustomer(getTenantId(), entityViewId));
logEntityAction(entityViewId, entityView, logEntityAction(entityViewId, entityView,
entityView.getCustomerId(), entityView.getCustomerId(),
@ -261,7 +269,7 @@ public class EntityViewController extends BaseController {
try { try {
TenantId tenantId = getCurrentUser().getTenantId(); TenantId tenantId = getCurrentUser().getTenantId();
CustomerId customerId = new CustomerId(toUUID(strCustomerId)); CustomerId customerId = new CustomerId(toUUID(strCustomerId));
checkCustomerId(customerId); checkCustomerId(customerId, Operation.READ);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
if (type != null && type.trim().length() > 0) { if (type != null && type.trim().length() > 0) {
return checkNotNull(entityViewService.findEntityViewsByTenantIdAndCustomerIdAndType(tenantId, customerId, pageLink, type)); return checkNotNull(entityViewService.findEntityViewsByTenantIdAndCustomerIdAndType(tenantId, customerId, pageLink, type));
@ -303,12 +311,12 @@ public class EntityViewController extends BaseController {
checkNotNull(query); checkNotNull(query);
checkNotNull(query.getParameters()); checkNotNull(query.getParameters());
checkNotNull(query.getEntityViewTypes()); checkNotNull(query.getEntityViewTypes());
checkEntityId(query.getParameters().getEntityId()); checkEntityId(query.getParameters().getEntityId(), Operation.READ);
try { try {
List<EntityView> entityViews = checkNotNull(entityViewService.findEntityViewsByQuery(getTenantId(), query).get()); List<EntityView> entityViews = checkNotNull(entityViewService.findEntityViewsByQuery(getTenantId(), query).get());
entityViews = entityViews.stream().filter(entityView -> { entityViews = entityViews.stream().filter(entityView -> {
try { try {
checkEntityView(entityView); accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, Operation.READ, entityView.getId(), entityView);
return true; return true;
} catch (ThingsboardException e) { } catch (ThingsboardException e) {
return false; return false;
@ -341,7 +349,7 @@ public class EntityViewController extends BaseController {
checkParameter(ENTITY_VIEW_ID, strEntityViewId); checkParameter(ENTITY_VIEW_ID, strEntityViewId);
try { try {
EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId)); EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
EntityView entityView = checkEntityViewId(entityViewId); EntityView entityView = checkEntityViewId(entityViewId, Operation.ASSIGN_TO_CUSTOMER);
Customer publicCustomer = customerService.findOrCreatePublicCustomer(entityView.getTenantId()); Customer publicCustomer = customerService.findOrCreatePublicCustomer(entityView.getTenantId());
EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToCustomer(getCurrentUser().getTenantId(), entityViewId, publicCustomer.getId())); EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToCustomer(getCurrentUser().getTenantId(), entityViewId, publicCustomer.getId()));

25
application/src/main/java/org/thingsboard/server/controller/EventController.java

@ -26,12 +26,15 @@ import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
@RestController @RestController
@RequestMapping("/api") @RequestMapping("/api")
@ -58,13 +61,12 @@ public class EventController extends BaseController {
checkParameter("EntityType", strEntityType); checkParameter("EntityType", strEntityType);
try { try {
TenantId tenantId = new TenantId(toUUID(strTenantId)); TenantId tenantId = new TenantId(toUUID(strTenantId));
if (!tenantId.getId().equals(ModelConstants.NULL_UUID) &&
!tenantId.equals(getCurrentUser().getTenantId())) { EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
throw new ThingsboardException("You don't have permission to perform this operation!", checkEntityId(entityId, Operation.READ);
ThingsboardErrorCode.PERMISSION_DENIED);
}
TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
return checkNotNull(eventService.findEvents(tenantId, EntityIdFactory.getByTypeAndId(strEntityType, strEntityId), eventType, pageLink)); return checkNotNull(eventService.findEvents(tenantId, entityId, eventType, pageLink));
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -87,13 +89,12 @@ public class EventController extends BaseController {
checkParameter("EntityType", strEntityType); checkParameter("EntityType", strEntityType);
try { try {
TenantId tenantId = new TenantId(toUUID(strTenantId)); TenantId tenantId = new TenantId(toUUID(strTenantId));
if (!tenantId.getId().equals(ModelConstants.NULL_UUID) &&
!tenantId.equals(getCurrentUser().getTenantId())) { EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
throw new ThingsboardException("You don't have permission to perform this operation!", checkEntityId(entityId, Operation.READ);
ThingsboardErrorCode.PERMISSION_DENIED);
}
TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
return checkNotNull(eventService.findEvents(tenantId, EntityIdFactory.getByTypeAndId(strEntityType, strEntityId), pageLink)); return checkNotNull(eventService.findEvents(tenantId, entityId, pageLink));
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }

3
application/src/main/java/org/thingsboard/server/controller/RpcController.java

@ -47,6 +47,7 @@ import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.service.rpc.LocalRequestMetaData; import org.thingsboard.server.service.rpc.LocalRequestMetaData;
import org.thingsboard.server.service.security.AccessValidator; import org.thingsboard.server.service.security.AccessValidator;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.telemetry.exception.ToErrorResponseEntity; import org.thingsboard.server.service.telemetry.exception.ToErrorResponseEntity;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -118,7 +119,7 @@ public class RpcController extends BaseController {
final DeferredResult<ResponseEntity> response = new DeferredResult<>(); final DeferredResult<ResponseEntity> response = new DeferredResult<>();
long timeout = System.currentTimeMillis() + (cmd.getTimeout() != null ? cmd.getTimeout() : DEFAULT_TIMEOUT); long timeout = System.currentTimeMillis() + (cmd.getTimeout() != null ? cmd.getTimeout() : DEFAULT_TIMEOUT);
ToDeviceRpcRequestBody body = new ToDeviceRpcRequestBody(cmd.getMethodName(), cmd.getRequestData()); ToDeviceRpcRequestBody body = new ToDeviceRpcRequestBody(cmd.getMethodName(), cmd.getRequestData());
accessValidator.validate(currentUser, deviceId, new HttpValidationCallback(response, new FutureCallback<DeferredResult<ResponseEntity>>() { accessValidator.validate(currentUser, Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback<DeferredResult<ResponseEntity>>() {
@Override @Override
public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) { public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) {
ToDeviceRpcRequest rpcRequest = new ToDeviceRpcRequest(UUID.randomUUID(), ToDeviceRpcRequest rpcRequest = new ToDeviceRpcRequest(UUID.randomUUID(),

31
application/src/main/java/org/thingsboard/server/controller/RuleChainController.java

@ -47,15 +47,20 @@ import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.service.script.JsInvokeService; import org.thingsboard.server.service.script.JsInvokeService;
import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
@Slf4j @Slf4j
@RestController @RestController
@ -80,7 +85,7 @@ public class RuleChainController extends BaseController {
checkParameter(RULE_CHAIN_ID, strRuleChainId); checkParameter(RULE_CHAIN_ID, strRuleChainId);
try { try {
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
return checkRuleChain(ruleChainId); return checkRuleChain(ruleChainId, Operation.READ);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -93,7 +98,7 @@ public class RuleChainController extends BaseController {
checkParameter(RULE_CHAIN_ID, strRuleChainId); checkParameter(RULE_CHAIN_ID, strRuleChainId);
try { try {
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
checkRuleChain(ruleChainId); checkRuleChain(ruleChainId, Operation.READ);
return ruleChainService.loadRuleChainMetaData(getTenantId(), ruleChainId); return ruleChainService.loadRuleChainMetaData(getTenantId(), ruleChainId);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
@ -108,6 +113,12 @@ public class RuleChainController extends BaseController {
try { try {
boolean created = ruleChain.getId() == null; boolean created = ruleChain.getId() == null;
ruleChain.setTenantId(getCurrentUser().getTenantId()); ruleChain.setTenantId(getCurrentUser().getTenantId());
Operation operation = created ? Operation.CREATE : Operation.WRITE;
accessControlService.checkPermission(getCurrentUser(), Resource.RULE_CHAIN, operation,
ruleChain.getId(), ruleChain);
RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain)); RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain));
actorService.onEntityStateChange(ruleChain.getTenantId(), savedRuleChain.getId(), actorService.onEntityStateChange(ruleChain.getTenantId(), savedRuleChain.getId(),
@ -134,7 +145,7 @@ public class RuleChainController extends BaseController {
checkParameter(RULE_CHAIN_ID, strRuleChainId); checkParameter(RULE_CHAIN_ID, strRuleChainId);
try { try {
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
RuleChain ruleChain = checkRuleChain(ruleChainId); RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE);
TenantId tenantId = getCurrentUser().getTenantId(); TenantId tenantId = getCurrentUser().getTenantId();
RuleChain previousRootRuleChain = ruleChainService.getRootTenantRuleChain(tenantId); RuleChain previousRootRuleChain = ruleChainService.getRootTenantRuleChain(tenantId);
if (ruleChainService.setRootRuleChain(getTenantId(), ruleChainId)) { if (ruleChainService.setRootRuleChain(getTenantId(), ruleChainId)) {
@ -171,7 +182,7 @@ public class RuleChainController extends BaseController {
@ResponseBody @ResponseBody
public RuleChainMetaData saveRuleChainMetaData(@RequestBody RuleChainMetaData ruleChainMetaData) throws ThingsboardException { public RuleChainMetaData saveRuleChainMetaData(@RequestBody RuleChainMetaData ruleChainMetaData) throws ThingsboardException {
try { try {
RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId()); RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId(), Operation.WRITE);
RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.saveRuleChainMetaData(getTenantId(), ruleChainMetaData)); RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.saveRuleChainMetaData(getTenantId(), ruleChainMetaData));
actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED); actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED);
@ -214,10 +225,19 @@ public class RuleChainController extends BaseController {
checkParameter(RULE_CHAIN_ID, strRuleChainId); checkParameter(RULE_CHAIN_ID, strRuleChainId);
try { try {
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
RuleChain ruleChain = checkRuleChain(ruleChainId); RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.DELETE);
List<RuleNode> referencingRuleNodes = ruleChainService.getReferencingRuleChainNodes(getTenantId(), ruleChainId);
Set<RuleChainId> referencingRuleChainIds = referencingRuleNodes.stream().map(RuleNode::getRuleChainId).collect(Collectors.toSet());
ruleChainService.deleteRuleChainById(getTenantId(), ruleChainId); ruleChainService.deleteRuleChainById(getTenantId(), ruleChainId);
referencingRuleChainIds.remove(ruleChain.getId());
referencingRuleChainIds.forEach(referencingRuleChainId ->
actorService.onEntityStateChange(ruleChain.getTenantId(), referencingRuleChainId, ComponentLifecycleEvent.UPDATED));
actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED); actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED);
logEntityAction(ruleChainId, ruleChain, logEntityAction(ruleChainId, ruleChain,
@ -240,6 +260,7 @@ public class RuleChainController extends BaseController {
checkParameter(RULE_NODE_ID, strRuleNodeId); checkParameter(RULE_NODE_ID, strRuleNodeId);
try { try {
RuleNodeId ruleNodeId = new RuleNodeId(toUUID(strRuleNodeId)); RuleNodeId ruleNodeId = new RuleNodeId(toUUID(strRuleNodeId));
checkRuleNode(ruleNodeId, Operation.READ);
TenantId tenantId = getCurrentUser().getTenantId(); TenantId tenantId = getCurrentUser().getTenantId();
List<Event> events = eventService.findLatestEvents(tenantId, ruleNodeId, DataConstants.DEBUG_RULE_NODE, 2); List<Event> events = eventService.findLatestEvents(tenantId, ruleNodeId, DataConstants.DEBUG_RULE_NODE, 2);
JsonNode result = null; JsonNode result = null;

23
application/src/main/java/org/thingsboard/server/controller/TelemetryController.java

@ -67,6 +67,7 @@ import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.service.security.AccessValidator; import org.thingsboard.server.service.security.AccessValidator;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.telemetry.AttributeData; import org.thingsboard.server.service.telemetry.AttributeData;
import org.thingsboard.server.service.telemetry.TsData; import org.thingsboard.server.service.telemetry.TsData;
import org.thingsboard.server.service.telemetry.exception.InvalidParametersException; import org.thingsboard.server.service.telemetry.exception.InvalidParametersException;
@ -122,7 +123,7 @@ public class TelemetryController extends BaseController {
@ResponseBody @ResponseBody
public DeferredResult<ResponseEntity> getAttributeKeys( public DeferredResult<ResponseEntity> getAttributeKeys(
@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr) throws ThingsboardException { @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr) throws ThingsboardException {
return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr, this::getAttributeKeysCallback); return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, this::getAttributeKeysCallback);
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -131,7 +132,7 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> getAttributeKeysByScope( public DeferredResult<ResponseEntity> getAttributeKeysByScope(
@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr
, @PathVariable("scope") String scope) throws ThingsboardException { , @PathVariable("scope") String scope) throws ThingsboardException {
return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr, return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
(result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope)); (result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope));
} }
@ -142,7 +143,7 @@ public class TelemetryController extends BaseController {
@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
@RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
SecurityUser user = getCurrentUser(); SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr, return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
(result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr)); (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr));
} }
@ -154,7 +155,7 @@ public class TelemetryController extends BaseController {
@PathVariable("scope") String scope, @PathVariable("scope") String scope,
@RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
SecurityUser user = getCurrentUser(); SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr, return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
(result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr)); (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr));
} }
@ -163,7 +164,7 @@ public class TelemetryController extends BaseController {
@ResponseBody @ResponseBody
public DeferredResult<ResponseEntity> getTimeseriesKeys( public DeferredResult<ResponseEntity> getTimeseriesKeys(
@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr) throws ThingsboardException { @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr) throws ThingsboardException {
return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr, return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr,
(result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result))); (result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result)));
} }
@ -175,7 +176,7 @@ public class TelemetryController extends BaseController {
@RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
SecurityUser user = getCurrentUser(); SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr, return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr,
(result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr)); (result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr));
} }
@ -192,7 +193,7 @@ public class TelemetryController extends BaseController {
@RequestParam(name = "limit", defaultValue = "100") Integer limit, @RequestParam(name = "limit", defaultValue = "100") Integer limit,
@RequestParam(name = "agg", defaultValue = "NONE") String aggStr @RequestParam(name = "agg", defaultValue = "NONE") String aggStr
) throws ThingsboardException { ) throws ThingsboardException {
return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr, return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr,
(result, tenantId, entityId) -> { (result, tenantId, entityId) -> {
// If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted // If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted
Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr); Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr);
@ -289,7 +290,7 @@ public class TelemetryController extends BaseController {
} }
} }
return accessValidator.validateEntityAndCallback(user, entityIdStr, (result, tenantId, entityId) -> { return accessValidator.validateEntityAndCallback(user, Operation.WRITE_TELEMETRY, entityIdStr, (result, tenantId, entityId) -> {
List<DeleteTsKvQuery> deleteTsKvQueries = new ArrayList<>(); List<DeleteTsKvQuery> deleteTsKvQueries = new ArrayList<>();
for (String key : keys) { for (String key : keys) {
deleteTsKvQueries.add(new BaseDeleteTsKvQuery(key, deleteFromTs, deleteToTs, rewriteLatestIfDeleted)); deleteTsKvQueries.add(new BaseDeleteTsKvQuery(key, deleteFromTs, deleteToTs, rewriteLatestIfDeleted));
@ -342,7 +343,7 @@ public class TelemetryController extends BaseController {
if (DataConstants.SERVER_SCOPE.equals(scope) || if (DataConstants.SERVER_SCOPE.equals(scope) ||
DataConstants.SHARED_SCOPE.equals(scope) || DataConstants.SHARED_SCOPE.equals(scope) ||
DataConstants.CLIENT_SCOPE.equals(scope)) { DataConstants.CLIENT_SCOPE.equals(scope)) {
return accessValidator.validateEntityAndCallback(getCurrentUser(), entityIdStr, (result, tenantId, entityId) -> { return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdStr, (result, tenantId, entityId) -> {
ListenableFuture<List<Void>> future = attributesService.removeAll(user.getTenantId(), entityId, scope, keys); ListenableFuture<List<Void>> future = attributesService.removeAll(user.getTenantId(), entityId, scope, keys);
Futures.addCallback(future, new FutureCallback<List<Void>>() { Futures.addCallback(future, new FutureCallback<List<Void>>() {
@Override @Override
@ -381,7 +382,7 @@ public class TelemetryController extends BaseController {
return getImmediateDeferredResult("No attributes data found in request body!", HttpStatus.BAD_REQUEST); return getImmediateDeferredResult("No attributes data found in request body!", HttpStatus.BAD_REQUEST);
} }
SecurityUser user = getCurrentUser(); SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), entityIdSrc, (result, tenantId, entityId) -> { return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> {
tsSubService.saveAndNotify(tenantId, entityId, scope, attributes, new FutureCallback<Void>() { tsSubService.saveAndNotify(tenantId, entityId, scope, attributes, new FutureCallback<Void>() {
@Override @Override
public void onSuccess(@Nullable Void tmp) { public void onSuccess(@Nullable Void tmp) {
@ -430,7 +431,7 @@ public class TelemetryController extends BaseController {
return getImmediateDeferredResult("No timeseries data found in request body!", HttpStatus.BAD_REQUEST); return getImmediateDeferredResult("No timeseries data found in request body!", HttpStatus.BAD_REQUEST);
} }
SecurityUser user = getCurrentUser(); SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), entityIdSrc, (result, tenantId, entityId) -> { return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_TELEMETRY, entityIdSrc, (result, tenantId, entityId) -> {
tsSubService.saveAndNotify(tenantId, entityId, entries, ttl, new FutureCallback<Void>() { tsSubService.saveAndNotify(tenantId, entityId, entries, ttl, new FutureCallback<Void>() {
@Override @Override
public void onSuccess(@Nullable Void tmp) { public void onSuccess(@Nullable Void tmp) {

11
application/src/main/java/org/thingsboard/server/controller/TenantController.java

@ -35,6 +35,8 @@ import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.service.install.InstallScripts; import org.thingsboard.server.service.install.InstallScripts;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
@RestController @RestController
@RequestMapping("/api") @RequestMapping("/api")
@ -54,7 +56,7 @@ public class TenantController extends BaseController {
checkParameter("tenantId", strTenantId); checkParameter("tenantId", strTenantId);
try { try {
TenantId tenantId = new TenantId(toUUID(strTenantId)); TenantId tenantId = new TenantId(toUUID(strTenantId));
checkTenantId(tenantId); checkTenantId(tenantId, Operation.READ);
return checkNotNull(tenantService.findTenantById(tenantId)); return checkNotNull(tenantService.findTenantById(tenantId));
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
@ -67,6 +69,12 @@ public class TenantController extends BaseController {
public Tenant saveTenant(@RequestBody Tenant tenant) throws ThingsboardException { public Tenant saveTenant(@RequestBody Tenant tenant) throws ThingsboardException {
try { try {
boolean newTenant = tenant.getId() == null; boolean newTenant = tenant.getId() == null;
Operation operation = newTenant ? Operation.CREATE : Operation.WRITE;
accessControlService.checkPermission(getCurrentUser(), Resource.TENANT, operation,
tenant.getId(), tenant);
tenant = checkNotNull(tenantService.saveTenant(tenant)); tenant = checkNotNull(tenantService.saveTenant(tenant));
if (newTenant) { if (newTenant) {
installScripts.createDefaultRuleChains(tenant.getId()); installScripts.createDefaultRuleChains(tenant.getId());
@ -84,6 +92,7 @@ public class TenantController extends BaseController {
checkParameter("tenantId", strTenantId); checkParameter("tenantId", strTenantId);
try { try {
TenantId tenantId = new TenantId(toUUID(strTenantId)); TenantId tenantId = new TenantId(toUUID(strTenantId));
checkTenantId(tenantId, Operation.DELETE);
tenantService.deleteTenant(tenantId); tenantService.deleteTenant(tenantId);
actorService.onEntityStateChange(tenantId, tenantId, ComponentLifecycleEvent.DELETED); actorService.onEntityStateChange(tenantId, tenantId, ComponentLifecycleEvent.DELETED);

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

@ -49,6 +49,8 @@ import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.UserPrincipal; import org.thingsboard.server.service.security.model.UserPrincipal;
import org.thingsboard.server.service.security.model.token.JwtToken; import org.thingsboard.server.service.security.model.token.JwtToken;
import org.thingsboard.server.service.security.model.token.JwtTokenFactory; import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -81,12 +83,7 @@ public class UserController extends BaseController {
checkParameter(USER_ID, strUserId); checkParameter(USER_ID, strUserId);
try { try {
UserId userId = new UserId(toUUID(strUserId)); UserId userId = new UserId(toUUID(strUserId));
SecurityUser authUser = getCurrentUser(); return checkUserId(userId, Operation.READ);
if (authUser.getAuthority() == Authority.CUSTOMER_USER && !authUser.getId().equals(userId)) {
throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION,
ThingsboardErrorCode.PERMISSION_DENIED);
}
return checkUserId(userId);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -105,14 +102,13 @@ public class UserController extends BaseController {
public JsonNode getUserToken(@PathVariable(USER_ID) String strUserId) throws ThingsboardException { public JsonNode getUserToken(@PathVariable(USER_ID) String strUserId) throws ThingsboardException {
checkParameter(USER_ID, strUserId); checkParameter(USER_ID, strUserId);
try { try {
UserId userId = new UserId(toUUID(strUserId)); if (!userTokenAccessEnabled) {
SecurityUser authUser = getCurrentUser();
User user = userService.findUserById(authUser.getTenantId(), userId);
if (!userTokenAccessEnabled || (authUser.getAuthority() == Authority.SYS_ADMIN && user.getAuthority() != Authority.TENANT_ADMIN)
|| (authUser.getAuthority() == Authority.TENANT_ADMIN && !authUser.getTenantId().equals(user.getTenantId()))) {
throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION,
ThingsboardErrorCode.PERMISSION_DENIED); ThingsboardErrorCode.PERMISSION_DENIED);
} }
UserId userId = new UserId(toUUID(strUserId));
SecurityUser authUser = getCurrentUser();
User user = checkUserId(userId, Operation.READ);
UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail());
UserCredentials credentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), userId); UserCredentials credentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), userId);
SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal); SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal);
@ -135,17 +131,20 @@ public class UserController extends BaseController {
@RequestParam(required = false, defaultValue = "true") boolean sendActivationMail, @RequestParam(required = false, defaultValue = "true") boolean sendActivationMail,
HttpServletRequest request) throws ThingsboardException { HttpServletRequest request) throws ThingsboardException {
try { try {
SecurityUser authUser = getCurrentUser();
if (authUser.getAuthority() == Authority.CUSTOMER_USER && !authUser.getId().equals(user.getId())) {
throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION,
ThingsboardErrorCode.PERMISSION_DENIED);
}
boolean sendEmail = user.getId() == null && sendActivationMail;
if (getCurrentUser().getAuthority() == Authority.TENANT_ADMIN) { if (getCurrentUser().getAuthority() == Authority.TENANT_ADMIN) {
user.setTenantId(getCurrentUser().getTenantId()); user.setTenantId(getCurrentUser().getTenantId());
} }
Operation operation = user.getId() == null ? Operation.CREATE : Operation.WRITE;
accessControlService.checkPermission(getCurrentUser(), Resource.USER, operation,
user.getId(), user);
boolean sendEmail = user.getId() == null && sendActivationMail;
User savedUser = checkNotNull(userService.saveUser(user)); User savedUser = checkNotNull(userService.saveUser(user));
if (sendEmail) { if (sendEmail) {
SecurityUser authUser = getCurrentUser();
UserCredentials userCredentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), savedUser.getId()); UserCredentials userCredentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), savedUser.getId());
String baseUrl = constructBaseUrl(request); String baseUrl = constructBaseUrl(request);
String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl,
@ -181,6 +180,10 @@ public class UserController extends BaseController {
HttpServletRequest request) throws ThingsboardException { HttpServletRequest request) throws ThingsboardException {
try { try {
User user = checkNotNull(userService.findUserByEmail(getCurrentUser().getTenantId(), email)); User user = checkNotNull(userService.findUserByEmail(getCurrentUser().getTenantId(), email));
accessControlService.checkPermission(getCurrentUser(), Resource.USER, Operation.READ,
user.getId(), user);
UserCredentials userCredentials = userService.findUserCredentialsByUserId(getCurrentUser().getTenantId(), user.getId()); UserCredentials userCredentials = userService.findUserCredentialsByUserId(getCurrentUser().getTenantId(), user.getId());
if (!userCredentials.isEnabled()) { if (!userCredentials.isEnabled()) {
String baseUrl = constructBaseUrl(request); String baseUrl = constructBaseUrl(request);
@ -204,13 +207,9 @@ public class UserController extends BaseController {
checkParameter(USER_ID, strUserId); checkParameter(USER_ID, strUserId);
try { try {
UserId userId = new UserId(toUUID(strUserId)); UserId userId = new UserId(toUUID(strUserId));
User user = checkUserId(userId, Operation.READ);
SecurityUser authUser = getCurrentUser(); SecurityUser authUser = getCurrentUser();
if (authUser.getAuthority() == Authority.CUSTOMER_USER && !authUser.getId().equals(userId)) { UserCredentials userCredentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), user.getId());
throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION,
ThingsboardErrorCode.PERMISSION_DENIED);
}
User user = checkUserId(userId);
UserCredentials userCredentials = userService.findUserCredentialsByUserId(getCurrentUser().getTenantId(), user.getId());
if (!userCredentials.isEnabled()) { if (!userCredentials.isEnabled()) {
String baseUrl = constructBaseUrl(request); String baseUrl = constructBaseUrl(request);
String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl,
@ -231,7 +230,7 @@ public class UserController extends BaseController {
checkParameter(USER_ID, strUserId); checkParameter(USER_ID, strUserId);
try { try {
UserId userId = new UserId(toUUID(strUserId)); UserId userId = new UserId(toUUID(strUserId));
User user = checkUserId(userId); User user = checkUserId(userId, Operation.DELETE);
userService.deleteUser(getCurrentUser().getTenantId(), userId); userService.deleteUser(getCurrentUser().getTenantId(), userId);
logEntityAction(userId, user, logEntityAction(userId, user,
@ -278,7 +277,7 @@ public class UserController extends BaseController {
checkParameter("customerId", strCustomerId); checkParameter("customerId", strCustomerId);
try { try {
CustomerId customerId = new CustomerId(toUUID(strCustomerId)); CustomerId customerId = new CustomerId(toUUID(strCustomerId));
checkCustomerId(customerId); checkCustomerId(customerId, Operation.READ);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
TenantId tenantId = getCurrentUser().getTenantId(); TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(userService.findCustomerUsers(tenantId, customerId, pageLink)); return checkNotNull(userService.findCustomerUsers(tenantId, customerId, pageLink));

19
application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java

@ -31,6 +31,8 @@ import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.List; import java.util.List;
@ -45,7 +47,7 @@ public class WidgetTypeController extends BaseController {
checkParameter("widgetTypeId", strWidgetTypeId); checkParameter("widgetTypeId", strWidgetTypeId);
try { try {
WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId)); WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId));
return checkWidgetTypeId(widgetTypeId, false); return checkWidgetTypeId(widgetTypeId, Operation.READ);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -57,10 +59,16 @@ public class WidgetTypeController extends BaseController {
public WidgetType saveWidgetType(@RequestBody WidgetType widgetType) throws ThingsboardException { public WidgetType saveWidgetType(@RequestBody WidgetType widgetType) throws ThingsboardException {
try { try {
if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) { if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) {
widgetType.setTenantId(new TenantId(ModelConstants.NULL_UUID)); widgetType.setTenantId(TenantId.SYS_TENANT_ID);
} else { } else {
widgetType.setTenantId(getCurrentUser().getTenantId()); widgetType.setTenantId(getCurrentUser().getTenantId());
} }
Operation operation = widgetType.getId() == null ? Operation.CREATE : Operation.WRITE;
accessControlService.checkPermission(getCurrentUser(), Resource.WIDGET_TYPE, operation,
widgetType.getId(), widgetType);
return checkNotNull(widgetTypeService.saveWidgetType(widgetType)); return checkNotNull(widgetTypeService.saveWidgetType(widgetType));
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
@ -74,7 +82,7 @@ public class WidgetTypeController extends BaseController {
checkParameter("widgetTypeId", strWidgetTypeId); checkParameter("widgetTypeId", strWidgetTypeId);
try { try {
WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId)); WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId));
checkWidgetTypeId(widgetTypeId, true); checkWidgetTypeId(widgetTypeId, Operation.DELETE);
widgetTypeService.deleteWidgetType(getCurrentUser().getTenantId(), widgetTypeId); widgetTypeService.deleteWidgetType(getCurrentUser().getTenantId(), widgetTypeId);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
@ -90,7 +98,7 @@ public class WidgetTypeController extends BaseController {
try { try {
TenantId tenantId; TenantId tenantId;
if (isSystem) { if (isSystem) {
tenantId = new TenantId(ModelConstants.NULL_UUID); tenantId = TenantId.SYS_TENANT_ID;
} else { } else {
tenantId = getCurrentUser().getTenantId(); tenantId = getCurrentUser().getTenantId();
} }
@ -115,7 +123,8 @@ public class WidgetTypeController extends BaseController {
tenantId = getCurrentUser().getTenantId(); tenantId = getCurrentUser().getTenantId();
} }
WidgetType widgetType = widgetTypeService.findWidgetTypeByTenantIdBundleAliasAndAlias(tenantId, bundleAlias, alias); WidgetType widgetType = widgetTypeService.findWidgetTypeByTenantIdBundleAliasAndAlias(tenantId, bundleAlias, alias);
checkWidgetType(widgetType, false); checkNotNull(widgetType);
accessControlService.checkPermission(getCurrentUser(), Resource.WIDGET_TYPE, Operation.READ, widgetType.getId(), widgetType);
return widgetType; return widgetType;
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);

14
application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java

@ -33,6 +33,8 @@ import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.List; import java.util.List;
@ -47,7 +49,7 @@ public class WidgetsBundleController extends BaseController {
checkParameter("widgetsBundleId", strWidgetsBundleId); checkParameter("widgetsBundleId", strWidgetsBundleId);
try { try {
WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId));
return checkWidgetsBundleId(widgetsBundleId, false); return checkWidgetsBundleId(widgetsBundleId, Operation.READ);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -59,10 +61,16 @@ public class WidgetsBundleController extends BaseController {
public WidgetsBundle saveWidgetsBundle(@RequestBody WidgetsBundle widgetsBundle) throws ThingsboardException { public WidgetsBundle saveWidgetsBundle(@RequestBody WidgetsBundle widgetsBundle) throws ThingsboardException {
try { try {
if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) { if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) {
widgetsBundle.setTenantId(new TenantId(ModelConstants.NULL_UUID)); widgetsBundle.setTenantId(TenantId.SYS_TENANT_ID);
} else { } else {
widgetsBundle.setTenantId(getCurrentUser().getTenantId()); widgetsBundle.setTenantId(getCurrentUser().getTenantId());
} }
Operation operation = widgetsBundle.getId() == null ? Operation.CREATE : Operation.WRITE;
accessControlService.checkPermission(getCurrentUser(), Resource.WIDGETS_BUNDLE, operation,
widgetsBundle.getId(), widgetsBundle);
return checkNotNull(widgetsBundleService.saveWidgetsBundle(widgetsBundle)); return checkNotNull(widgetsBundleService.saveWidgetsBundle(widgetsBundle));
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
@ -76,7 +84,7 @@ public class WidgetsBundleController extends BaseController {
checkParameter("widgetsBundleId", strWidgetsBundleId); checkParameter("widgetsBundleId", strWidgetsBundleId);
try { try {
WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId));
checkWidgetsBundleId(widgetsBundleId, true); checkWidgetsBundleId(widgetsBundleId, Operation.DELETE);
widgetsBundleService.deleteWidgetsBundle(getTenantId(), widgetsBundleId); widgetsBundleService.deleteWidgetsBundle(getTenantId(), widgetsBundleId);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);

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

@ -23,7 +23,7 @@ import org.springframework.context.ApplicationContext;
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.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.install.DataUpdateService; import org.thingsboard.server.service.install.update.DataUpdateService;
import org.thingsboard.server.service.install.DatabaseUpgradeService; import org.thingsboard.server.service.install.DatabaseUpgradeService;
import org.thingsboard.server.service.install.EntityDatabaseSchemaService; import org.thingsboard.server.service.install.EntityDatabaseSchemaService;
import org.thingsboard.server.service.install.SystemDataLoaderService; import org.thingsboard.server.service.install.SystemDataLoaderService;
@ -106,6 +106,9 @@ public class ThingsboardInstallService {
databaseUpgradeService.upgradeDatabase("2.1.3"); databaseUpgradeService.upgradeDatabase("2.1.3");
case "2.2.0":
log.info("Upgrading ThingsBoard from version 2.2.0 to 2.3.0 ...");
log.info("Updating system data..."); log.info("Updating system data...");
systemDataLoaderService.deleteSystemWidgetBundle("charts"); systemDataLoaderService.deleteSystemWidgetBundle("charts");
@ -121,7 +124,6 @@ public class ThingsboardInstallService {
systemDataLoaderService.deleteSystemWidgetBundle("input_widgets"); systemDataLoaderService.deleteSystemWidgetBundle("input_widgets");
systemDataLoaderService.loadSystemWidgets(); systemDataLoaderService.loadSystemWidgets();
break; break;
default: default:
throw new RuntimeException("Unable to upgrade ThingsBoard, unsupported fromVersion: " + upgradeFromVersion); throw new RuntimeException("Unable to upgrade ThingsBoard, unsupported fromVersion: " + upgradeFromVersion);

2
application/src/main/java/org/thingsboard/server/service/install/DataUpdateService.java → application/src/main/java/org/thingsboard/server/service/install/update/DataUpdateService.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.service.install; package org.thingsboard.server.service.install.update;
public interface DataUpdateService { public interface DataUpdateService {

28
application/src/main/java/org/thingsboard/server/service/install/DefaultDataUpdateService.java → application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.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.service.install; package org.thingsboard.server.service.install.update;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
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.service.install.InstallScripts;
@Service @Service
@Profile("install") @Profile("install")
@ -75,29 +76,4 @@ public class DefaultDataUpdateService implements DataUpdateService {
} }
}; };
public abstract class PaginatedUpdater<I, D extends SearchTextBased<? extends UUIDBased>> {
private static final int DEFAULT_LIMIT = 100;
public void updateEntities(I id) {
TextPageLink pageLink = new TextPageLink(DEFAULT_LIMIT);
boolean hasNext = true;
while (hasNext) {
TextPageData<D> entities = findEntities(id, pageLink);
for (D entity : entities.getData()) {
updateEntity(entity);
}
hasNext = entities.hasNext();
if (hasNext) {
pageLink = entities.getNextPageLink();
}
}
}
protected abstract TextPageData<D> findEntities(I id, TextPageLink pageLink);
protected abstract void updateEntity(D entity);
}
} }

46
application/src/main/java/org/thingsboard/server/service/install/update/PaginatedUpdater.java

@ -0,0 +1,46 @@
/**
* Copyright © 2016-2019 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.install.update;
import org.thingsboard.server.common.data.SearchTextBased;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
public abstract class PaginatedUpdater<I, D extends SearchTextBased<? extends UUIDBased>> {
private static final int DEFAULT_LIMIT = 100;
public void updateEntities(I id) {
TextPageLink pageLink = new TextPageLink(DEFAULT_LIMIT);
boolean hasNext = true;
while (hasNext) {
TextPageData<D> entities = findEntities(id, pageLink);
for (D entity : entities.getData()) {
updateEntity(entity);
}
hasNext = entities.hasNext();
if (hasNext) {
pageLink = entities.getNextPageLink();
}
}
}
protected abstract TextPageData<D> findEntities(I id, TextPageLink pageLink);
protected abstract void updateEntity(D entity);
}

127
application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java

@ -51,6 +51,9 @@ 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.user.UserService; import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.AccessControlService;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import org.thingsboard.server.service.telemetry.exception.ToErrorResponseEntity; import org.thingsboard.server.service.telemetry.exception.ToErrorResponseEntity;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -95,6 +98,9 @@ public class AccessValidator {
@Autowired @Autowired
protected EntityViewService entityViewService; protected EntityViewService entityViewService;
@Autowired
protected AccessControlService accessControlService;
private ExecutorService executor; private ExecutorService executor;
@PostConstruct @PostConstruct
@ -109,30 +115,30 @@ public class AccessValidator {
} }
} }
public DeferredResult<ResponseEntity> validateEntityAndCallback(SecurityUser currentUser, String entityType, String entityIdStr, public DeferredResult<ResponseEntity> validateEntityAndCallback(SecurityUser currentUser, Operation operation, String entityType, String entityIdStr,
ThreeConsumer<DeferredResult<ResponseEntity>, TenantId, EntityId> onSuccess) throws ThingsboardException { ThreeConsumer<DeferredResult<ResponseEntity>, TenantId, EntityId> onSuccess) throws ThingsboardException {
return validateEntityAndCallback(currentUser, entityType, entityIdStr, onSuccess, (result, t) -> handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR)); return validateEntityAndCallback(currentUser, operation, entityType, entityIdStr, onSuccess, (result, t) -> handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR));
} }
public DeferredResult<ResponseEntity> validateEntityAndCallback(SecurityUser currentUser, String entityType, String entityIdStr, public DeferredResult<ResponseEntity> validateEntityAndCallback(SecurityUser currentUser, Operation operation, String entityType, String entityIdStr,
ThreeConsumer<DeferredResult<ResponseEntity>, TenantId, EntityId> onSuccess, ThreeConsumer<DeferredResult<ResponseEntity>, TenantId, EntityId> onSuccess,
BiConsumer<DeferredResult<ResponseEntity>, Throwable> onFailure) throws ThingsboardException { BiConsumer<DeferredResult<ResponseEntity>, Throwable> onFailure) throws ThingsboardException {
return validateEntityAndCallback(currentUser, EntityIdFactory.getByTypeAndId(entityType, entityIdStr), return validateEntityAndCallback(currentUser, operation, EntityIdFactory.getByTypeAndId(entityType, entityIdStr),
onSuccess, onFailure); onSuccess, onFailure);
} }
public DeferredResult<ResponseEntity> validateEntityAndCallback(SecurityUser currentUser, EntityId entityId, public DeferredResult<ResponseEntity> validateEntityAndCallback(SecurityUser currentUser, Operation operation, EntityId entityId,
ThreeConsumer<DeferredResult<ResponseEntity>, TenantId, EntityId> onSuccess) throws ThingsboardException { ThreeConsumer<DeferredResult<ResponseEntity>, TenantId, EntityId> onSuccess) throws ThingsboardException {
return validateEntityAndCallback(currentUser, entityId, onSuccess, (result, t) -> handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR)); return validateEntityAndCallback(currentUser, operation, entityId, onSuccess, (result, t) -> handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR));
} }
public DeferredResult<ResponseEntity> validateEntityAndCallback(SecurityUser currentUser, EntityId entityId, public DeferredResult<ResponseEntity> validateEntityAndCallback(SecurityUser currentUser, Operation operation, EntityId entityId,
ThreeConsumer<DeferredResult<ResponseEntity>, TenantId, EntityId> onSuccess, ThreeConsumer<DeferredResult<ResponseEntity>, TenantId, EntityId> onSuccess,
BiConsumer<DeferredResult<ResponseEntity>, Throwable> onFailure) throws ThingsboardException { BiConsumer<DeferredResult<ResponseEntity>, Throwable> onFailure) throws ThingsboardException {
final DeferredResult<ResponseEntity> response = new DeferredResult<>(); final DeferredResult<ResponseEntity> response = new DeferredResult<>();
validate(currentUser, entityId, new HttpValidationCallback(response, validate(currentUser, operation, entityId, new HttpValidationCallback(response,
new FutureCallback<DeferredResult<ResponseEntity>>() { new FutureCallback<DeferredResult<ResponseEntity>>() {
@Override @Override
public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) { public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) {
@ -148,25 +154,25 @@ public class AccessValidator {
return response; return response;
} }
public void validate(SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { public void validate(SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
switch (entityId.getEntityType()) { switch (entityId.getEntityType()) {
case DEVICE: case DEVICE:
validateDevice(currentUser, entityId, callback); validateDevice(currentUser, operation, entityId, callback);
return; return;
case ASSET: case ASSET:
validateAsset(currentUser, entityId, callback); validateAsset(currentUser, operation, entityId, callback);
return; return;
case RULE_CHAIN: case RULE_CHAIN:
validateRuleChain(currentUser, entityId, callback); validateRuleChain(currentUser, operation, entityId, callback);
return; return;
case CUSTOMER: case CUSTOMER:
validateCustomer(currentUser, entityId, callback); validateCustomer(currentUser, operation, entityId, callback);
return; return;
case TENANT: case TENANT:
validateTenant(currentUser, entityId, callback); validateTenant(currentUser, operation, entityId, callback);
return; return;
case ENTITY_VIEW: case ENTITY_VIEW:
validateEntityView(currentUser, entityId, callback); validateEntityView(currentUser, operation, entityId, callback);
return; return;
default: default:
//TODO: add support of other entities //TODO: add support of other entities
@ -174,7 +180,7 @@ public class AccessValidator {
} }
} }
private void validateDevice(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { private void validateDevice(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
if (currentUser.isSystemAdmin()) { if (currentUser.isSystemAdmin()) {
callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
} else { } else {
@ -183,19 +189,18 @@ public class AccessValidator {
if (device == null) { if (device == null) {
return ValidationResult.entityNotFound(DEVICE_WITH_REQUESTED_ID_NOT_FOUND); return ValidationResult.entityNotFound(DEVICE_WITH_REQUESTED_ID_NOT_FOUND);
} else { } else {
if (!device.getTenantId().equals(currentUser.getTenantId())) { try {
return ValidationResult.accessDenied("Device doesn't belong to the current Tenant!"); accessControlService.checkPermission(currentUser, Resource.DEVICE, operation, entityId, device);
} else if (currentUser.isCustomerUser() && !device.getCustomerId().equals(currentUser.getCustomerId())) { } catch (ThingsboardException e) {
return ValidationResult.accessDenied("Device doesn't belong to the current Customer!"); return ValidationResult.accessDenied(e.getMessage());
} else {
return ValidationResult.ok(device);
} }
return ValidationResult.ok(device);
} }
}), executor); }), executor);
} }
} }
private void validateAsset(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { private void validateAsset(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
if (currentUser.isSystemAdmin()) { if (currentUser.isSystemAdmin()) {
callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
} else { } else {
@ -204,19 +209,18 @@ public class AccessValidator {
if (asset == null) { if (asset == null) {
return ValidationResult.entityNotFound("Asset with requested id wasn't found!"); return ValidationResult.entityNotFound("Asset with requested id wasn't found!");
} else { } else {
if (!asset.getTenantId().equals(currentUser.getTenantId())) { try {
return ValidationResult.accessDenied("Asset doesn't belong to the current Tenant!"); accessControlService.checkPermission(currentUser, Resource.ASSET, operation, entityId, asset);
} else if (currentUser.isCustomerUser() && !asset.getCustomerId().equals(currentUser.getCustomerId())) { } catch (ThingsboardException e) {
return ValidationResult.accessDenied("Asset doesn't belong to the current Customer!"); return ValidationResult.accessDenied(e.getMessage());
} else {
return ValidationResult.ok(asset);
} }
return ValidationResult.ok(asset);
} }
}), executor); }), executor);
} }
} }
private void validateRuleChain(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { private void validateRuleChain(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
if (currentUser.isCustomerUser()) { if (currentUser.isCustomerUser()) {
callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
} else { } else {
@ -225,19 +229,18 @@ public class AccessValidator {
if (ruleChain == null) { if (ruleChain == null) {
return ValidationResult.entityNotFound("Rule chain with requested id wasn't found!"); return ValidationResult.entityNotFound("Rule chain with requested id wasn't found!");
} else { } else {
if (currentUser.isTenantAdmin() && !ruleChain.getTenantId().equals(currentUser.getTenantId())) { try {
return ValidationResult.accessDenied("Rule chain doesn't belong to the current Tenant!"); accessControlService.checkPermission(currentUser, Resource.RULE_CHAIN, operation, entityId, ruleChain);
} else if (currentUser.isSystemAdmin() && !ruleChain.getTenantId().isNullUid()) { } catch (ThingsboardException e) {
return ValidationResult.accessDenied("Rule chain is not in system scope!"); return ValidationResult.accessDenied(e.getMessage());
} else {
return ValidationResult.ok(ruleChain);
} }
return ValidationResult.ok(ruleChain);
} }
}), executor); }), executor);
} }
} }
private void validateRule(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { private void validateRule(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
if (currentUser.isCustomerUser()) { if (currentUser.isCustomerUser()) {
callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
} else { } else {
@ -251,19 +254,18 @@ public class AccessValidator {
} else { } else {
//TODO: make async //TODO: make async
RuleChain ruleChain = ruleChainService.findRuleChainById(currentUser.getTenantId(), ruleNode.getRuleChainId()); RuleChain ruleChain = ruleChainService.findRuleChainById(currentUser.getTenantId(), ruleNode.getRuleChainId());
if (currentUser.isTenantAdmin() && !ruleChain.getTenantId().equals(currentUser.getTenantId())) { try {
return ValidationResult.accessDenied("Rule chain doesn't belong to the current Tenant!"); accessControlService.checkPermission(currentUser, Resource.RULE_CHAIN, operation, ruleNode.getRuleChainId(), ruleChain);
} else if (currentUser.isSystemAdmin() && !ruleChain.getTenantId().isNullUid()) { } catch (ThingsboardException e) {
return ValidationResult.accessDenied("Rule chain is not in system scope!"); return ValidationResult.accessDenied(e.getMessage());
} else {
return ValidationResult.ok(ruleNode);
} }
return ValidationResult.ok(ruleNode);
} }
}), executor); }), executor);
} }
} }
private void validateCustomer(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { private void validateCustomer(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
if (currentUser.isSystemAdmin()) { if (currentUser.isSystemAdmin()) {
callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
} else { } else {
@ -272,19 +274,18 @@ public class AccessValidator {
if (customer == null) { if (customer == null) {
return ValidationResult.entityNotFound("Customer with requested id wasn't found!"); return ValidationResult.entityNotFound("Customer with requested id wasn't found!");
} else { } else {
if (!customer.getTenantId().equals(currentUser.getTenantId())) { try {
return ValidationResult.accessDenied("Customer doesn't belong to the current Tenant!"); accessControlService.checkPermission(currentUser, Resource.CUSTOMER, operation, entityId, customer);
} else if (currentUser.isCustomerUser() && !customer.getId().equals(currentUser.getCustomerId())) { } catch (ThingsboardException e) {
return ValidationResult.accessDenied("Customer doesn't relate to the currently authorized customer user!"); return ValidationResult.accessDenied(e.getMessage());
} else {
return ValidationResult.ok(customer);
} }
return ValidationResult.ok(customer);
} }
}), executor); }), executor);
} }
} }
private void validateTenant(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { private void validateTenant(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
if (currentUser.isCustomerUser()) { if (currentUser.isCustomerUser()) {
callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
} else if (currentUser.isSystemAdmin()) { } else if (currentUser.isSystemAdmin()) {
@ -294,16 +295,19 @@ public class AccessValidator {
Futures.addCallback(tenantFuture, getCallback(callback, tenant -> { Futures.addCallback(tenantFuture, getCallback(callback, tenant -> {
if (tenant == null) { if (tenant == null) {
return ValidationResult.entityNotFound("Tenant with requested id wasn't found!"); return ValidationResult.entityNotFound("Tenant with requested id wasn't found!");
} else if (!tenant.getId().equals(currentUser.getTenantId())) {
return ValidationResult.accessDenied("Tenant doesn't relate to the currently authorized user!");
} else {
return ValidationResult.ok(tenant);
} }
try {
accessControlService.checkPermission(currentUser, Resource.TENANT, operation, entityId, tenant);
} catch (ThingsboardException e) {
return ValidationResult.accessDenied(e.getMessage());
}
return ValidationResult.ok(tenant);
}), executor); }), executor);
} }
} }
private void validateEntityView(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { private void validateEntityView(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
if (currentUser.isSystemAdmin()) { if (currentUser.isSystemAdmin()) {
callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
} else { } else {
@ -312,13 +316,12 @@ public class AccessValidator {
if (entityView == null) { if (entityView == null) {
return ValidationResult.entityNotFound(ENTITY_VIEW_WITH_REQUESTED_ID_NOT_FOUND); return ValidationResult.entityNotFound(ENTITY_VIEW_WITH_REQUESTED_ID_NOT_FOUND);
} else { } else {
if (!entityView.getTenantId().equals(currentUser.getTenantId())) { try {
return ValidationResult.accessDenied("Entity-view doesn't belong to the current Tenant!"); accessControlService.checkPermission(currentUser, Resource.ENTITY_VIEW, operation, entityId, entityView);
} else if (currentUser.isCustomerUser() && !entityView.getCustomerId().equals(currentUser.getCustomerId())) { } catch (ThingsboardException e) {
return ValidationResult.accessDenied("Entity-view doesn't belong to the current Customer!"); return ValidationResult.accessDenied(e.getMessage());
} else {
return ValidationResult.ok(entityView);
} }
return ValidationResult.ok(entityView);
} }
}), executor); }), executor);
} }

32
application/src/main/java/org/thingsboard/server/service/security/permission/AbstractPermissions.java

@ -0,0 +1,32 @@
/**
* Copyright © 2016-2019 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.permission;
import java.util.HashMap;
import java.util.Optional;
public abstract class AbstractPermissions extends HashMap<Resource, PermissionChecker> implements Permissions {
public AbstractPermissions() {
super();
}
@Override
public Optional<PermissionChecker> getPermissionChecker(Resource resource) {
PermissionChecker permissionChecker = this.get(resource);
return Optional.ofNullable(permissionChecker);
}
}

31
application/src/main/java/org/thingsboard/server/service/security/permission/AccessControlService.java

@ -0,0 +1,31 @@
/**
* Copyright © 2016-2019 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.permission;
import org.thingsboard.server.common.data.HasCustomerId;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.service.security.model.SecurityUser;
public interface AccessControlService {
void checkPermission(SecurityUser user, Resource resource, Operation operation) throws ThingsboardException;
<I extends EntityId, T extends HasTenantId> void checkPermission(SecurityUser user, Resource resource, Operation operation, I entityId, T entity) throws ThingsboardException;
}

135
application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPremissions.java

@ -0,0 +1,135 @@
/**
* Copyright © 2016-2019 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.permission;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.service.security.model.SecurityUser;
import java.util.HashMap;
@Component(value="customerUserPermissions")
public class CustomerUserPremissions extends AbstractPermissions {
public CustomerUserPremissions() {
super();
put(Resource.ALARM, TenantAdminPermissions.tenantEntityPermissionChecker);
put(Resource.ASSET, customerEntityPermissionChecker);
put(Resource.DEVICE, customerEntityPermissionChecker);
put(Resource.CUSTOMER, customerPermissionChecker);
put(Resource.DASHBOARD, customerDashboardPermissionChecker);
put(Resource.ENTITY_VIEW, customerEntityPermissionChecker);
put(Resource.USER, userPermissionChecker);
put(Resource.WIDGETS_BUNDLE, widgetsPermissionChecker);
put(Resource.WIDGET_TYPE, widgetsPermissionChecker);
}
private static final PermissionChecker customerEntityPermissionChecker =
new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_CREDENTIALS, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY) {
@Override
public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
if (!super.hasPermission(user, operation, entityId, entity)) {
return false;
}
if (!user.getTenantId().equals(entity.getTenantId())) {
return false;
}
if (!(entity instanceof HasCustomerId)) {
return false;
}
if (!user.getCustomerId().equals(((HasCustomerId)entity).getCustomerId())) {
return false;
}
return true;
}
};
private static final PermissionChecker customerPermissionChecker =
new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY) {
@Override
public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
if (!super.hasPermission(user, operation, entityId, entity)) {
return false;
}
if (!user.getCustomerId().equals(entityId)) {
return false;
}
return true;
}
};
private static final PermissionChecker customerDashboardPermissionChecker =
new PermissionChecker.GenericPermissionChecker<DashboardId, DashboardInfo>(Operation.READ, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY) {
@Override
public boolean hasPermission(SecurityUser user, Operation operation, DashboardId dashboardId, DashboardInfo dashboard) {
if (!super.hasPermission(user, operation, dashboardId, dashboard)) {
return false;
}
if (!user.getTenantId().equals(dashboard.getTenantId())) {
return false;
}
if (!dashboard.isAssignedToCustomer(user.getCustomerId())) {
return false;
}
return true;
}
};
private static final PermissionChecker userPermissionChecker = new PermissionChecker<UserId, User>() {
@Override
public boolean hasPermission(SecurityUser user, Operation operation, UserId userId, User userEntity) {
if (userEntity.getAuthority() != Authority.CUSTOMER_USER) {
return false;
}
if (!user.getId().equals(userId)) {
return false;
}
return true;
}
};
private static final PermissionChecker widgetsPermissionChecker = new PermissionChecker.GenericPermissionChecker(Operation.READ) {
@Override
public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
if (!super.hasPermission(user, operation, entityId, entity)) {
return false;
}
if (entity.getTenantId() == null || entity.getTenantId().isNullUid()) {
return true;
}
if (!user.getTenantId().equals(entity.getTenantId())) {
return false;
}
return true;
}
};
}

91
application/src/main/java/org/thingsboard/server/service/security/permission/DefaultAccessControlService.java

@ -0,0 +1,91 @@
/**
* Copyright © 2016-2019 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.permission;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HasCustomerId;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.service.security.model.SecurityUser;
import java.util.*;
import static org.thingsboard.server.dao.service.Validator.validateId;
@Service
@Slf4j
public class DefaultAccessControlService implements AccessControlService {
private static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
private static final String YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION = "You don't have permission to perform this operation!";
private final Map<Authority, Permissions> authorityPermissions = new HashMap<>();
public DefaultAccessControlService(
@Qualifier("sysAdminPermissions") Permissions sysAdminPermissions,
@Qualifier("tenantAdminPermissions") Permissions tenantAdminPermissions,
@Qualifier("customerUserPermissions") Permissions customerUserPermissions) {
authorityPermissions.put(Authority.SYS_ADMIN, sysAdminPermissions);
authorityPermissions.put(Authority.TENANT_ADMIN, tenantAdminPermissions);
authorityPermissions.put(Authority.CUSTOMER_USER, customerUserPermissions);
}
@Override
public void checkPermission(SecurityUser user, Resource resource, Operation operation) throws ThingsboardException {
PermissionChecker permissionChecker = getPermissionChecker(user.getAuthority(), resource);
if (!permissionChecker.hasPermission(user, operation)) {
permissionDenied();
}
}
@Override
public <I extends EntityId, T extends HasTenantId> void checkPermission(SecurityUser user, Resource resource,
Operation operation, I entityId, T entity) throws ThingsboardException {
PermissionChecker permissionChecker = getPermissionChecker(user.getAuthority(), resource);
if (!permissionChecker.hasPermission(user, operation, entityId, entity)) {
permissionDenied();
}
}
private PermissionChecker getPermissionChecker(Authority authority, Resource resource) throws ThingsboardException {
Permissions permissions = authorityPermissions.get(authority);
if (permissions == null) {
permissionDenied();
}
Optional<PermissionChecker> permissionChecker = permissions.getPermissionChecker(resource);
if (!permissionChecker.isPresent()) {
permissionDenied();
}
return permissionChecker.get();
}
private void permissionDenied() throws ThingsboardException {
throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION,
ThingsboardErrorCode.PERMISSION_DENIED);
}
}

23
application/src/main/java/org/thingsboard/server/service/security/permission/Operation.java

@ -0,0 +1,23 @@
/**
* Copyright © 2016-2019 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.permission;
public enum Operation {
ALL, CREATE, READ, WRITE, DELETE, ASSIGN_TO_CUSTOMER, UNASSIGN_FROM_CUSTOMER, RPC_CALL,
READ_CREDENTIALS, WRITE_CREDENTIALS, READ_ATTRIBUTES, WRITE_ATTRIBUTES, READ_TELEMETRY, WRITE_TELEMETRY
}

73
application/src/main/java/org/thingsboard/server/service/security/permission/PermissionChecker.java

@ -0,0 +1,73 @@
/**
* Copyright © 2016-2019 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.permission;
import org.thingsboard.server.common.data.HasCustomerId;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.service.security.model.SecurityUser;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public interface PermissionChecker<I extends EntityId, T extends HasTenantId> {
default boolean hasPermission(SecurityUser user, Operation operation) {
return false;
}
default boolean hasPermission(SecurityUser user, Operation operation, I entityId, T entity) {
return false;
}
public class GenericPermissionChecker<I extends EntityId, T extends HasTenantId> implements PermissionChecker<I,T> {
private final Set<Operation> allowedOperations;
public GenericPermissionChecker(Operation... operations) {
allowedOperations = new HashSet<Operation>(Arrays.asList(operations));
}
@Override
public boolean hasPermission(SecurityUser user, Operation operation) {
return allowedOperations.contains(Operation.ALL) || allowedOperations.contains(operation);
}
@Override
public boolean hasPermission(SecurityUser user, Operation operation, I entityId, T entity) {
return allowedOperations.contains(Operation.ALL) || allowedOperations.contains(operation);
}
}
public static PermissionChecker denyAllPermissionChecker = new PermissionChecker() {};
public static PermissionChecker allowAllPermissionChecker = new PermissionChecker<EntityId, HasTenantId>() {
@Override
public boolean hasPermission(SecurityUser user, Operation operation) {
return true;
}
@Override
public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
return true;
}
};
}

24
application/src/main/java/org/thingsboard/server/service/security/permission/Permissions.java

@ -0,0 +1,24 @@
/**
* Copyright © 2016-2019 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.permission;
import java.util.Optional;
public interface Permissions {
Optional<PermissionChecker> getPermissionChecker(Resource resource);
}

49
application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java

@ -0,0 +1,49 @@
/**
* Copyright © 2016-2019 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.permission;
import org.thingsboard.server.common.data.EntityType;
import java.util.Optional;
public enum Resource {
ADMIN_SETTINGS(),
ALARM(EntityType.ALARM),
DEVICE(EntityType.DEVICE),
ASSET(EntityType.ASSET),
CUSTOMER(EntityType.CUSTOMER),
DASHBOARD(EntityType.DASHBOARD),
ENTITY_VIEW(EntityType.ENTITY_VIEW),
TENANT(EntityType.TENANT),
RULE_CHAIN(EntityType.RULE_CHAIN),
USER(EntityType.USER),
WIDGETS_BUNDLE(EntityType.WIDGETS_BUNDLE),
WIDGET_TYPE(EntityType.WIDGET_TYPE);
private final EntityType entityType;
Resource() {
this.entityType = null;
}
Resource(EntityType entityType) {
this.entityType = entityType;
}
public Optional<EntityType> getEntityType() {
return Optional.ofNullable(entityType);
}
}

68
application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java

@ -0,0 +1,68 @@
/**
* Copyright © 2016-2019 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.permission;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.service.security.model.SecurityUser;
import java.util.HashMap;
import java.util.Optional;
@Component(value="sysAdminPermissions")
public class SysAdminPermissions extends AbstractPermissions {
public SysAdminPermissions() {
super();
put(Resource.ADMIN_SETTINGS, PermissionChecker.allowAllPermissionChecker);
put(Resource.DASHBOARD, new PermissionChecker.GenericPermissionChecker(Operation.READ));
put(Resource.TENANT, PermissionChecker.allowAllPermissionChecker);
put(Resource.RULE_CHAIN, systemEntityPermissionChecker);
put(Resource.USER, userPermissionChecker);
put(Resource.WIDGETS_BUNDLE, systemEntityPermissionChecker);
put(Resource.WIDGET_TYPE, systemEntityPermissionChecker);
}
private static final PermissionChecker systemEntityPermissionChecker = new PermissionChecker() {
@Override
public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
if (entity.getTenantId() != null && !entity.getTenantId().isNullUid()) {
return false;
}
return true;
}
};
private static final PermissionChecker userPermissionChecker = new PermissionChecker<UserId, User>() {
@Override
public boolean hasPermission(SecurityUser user, Operation operation, UserId userId, User userEntity) {
if (userEntity.getAuthority() == Authority.CUSTOMER_USER) {
return false;
}
return true;
}
};
}

104
application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java

@ -0,0 +1,104 @@
/**
* Copyright © 2016-2019 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.permission;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.service.security.model.SecurityUser;
import java.util.HashMap;
@Component(value="tenantAdminPermissions")
public class TenantAdminPermissions extends AbstractPermissions {
public TenantAdminPermissions() {
super();
put(Resource.ALARM, tenantEntityPermissionChecker);
put(Resource.ASSET, tenantEntityPermissionChecker);
put(Resource.DEVICE, tenantEntityPermissionChecker);
put(Resource.CUSTOMER, tenantEntityPermissionChecker);
put(Resource.DASHBOARD, tenantEntityPermissionChecker);
put(Resource.ENTITY_VIEW, tenantEntityPermissionChecker);
put(Resource.TENANT, tenantPermissionChecker);
put(Resource.RULE_CHAIN, tenantEntityPermissionChecker);
put(Resource.USER, userPermissionChecker);
put(Resource.WIDGETS_BUNDLE, widgetsPermissionChecker);
put(Resource.WIDGET_TYPE, widgetsPermissionChecker);
}
public static final PermissionChecker tenantEntityPermissionChecker = new PermissionChecker() {
@Override
public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
if (!user.getTenantId().equals(entity.getTenantId())) {
return false;
}
return true;
}
};
private static final PermissionChecker tenantPermissionChecker =
new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY) {
@Override
public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
if (!super.hasPermission(user, operation, entityId, entity)) {
return false;
}
if (!user.getTenantId().equals(entityId)) {
return false;
}
return true;
}
};
private static final PermissionChecker userPermissionChecker = new PermissionChecker<UserId, User>() {
@Override
public boolean hasPermission(SecurityUser user, Operation operation, UserId userId, User userEntity) {
if (userEntity.getAuthority() == Authority.SYS_ADMIN) {
return false;
}
if (!user.getTenantId().equals(userEntity.getTenantId())) {
return false;
}
return true;
}
};
private static final PermissionChecker widgetsPermissionChecker = new PermissionChecker() {
@Override
public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
if (entity.getTenantId() == null || entity.getTenantId().isNullUid()) {
return operation == Operation.READ;
}
if (!user.getTenantId().equals(entity.getTenantId())) {
return false;
}
return true;
}
};
}

17
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java

@ -48,6 +48,7 @@ import org.thingsboard.server.service.security.ValidationCallback;
import org.thingsboard.server.service.security.ValidationResult; import org.thingsboard.server.service.security.ValidationResult;
import org.thingsboard.server.service.security.ValidationResultCode; import org.thingsboard.server.service.security.ValidationResultCode;
import org.thingsboard.server.service.security.model.UserPrincipal; import org.thingsboard.server.service.security.model.UserPrincipal;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.telemetry.cmd.AttributesSubscriptionCmd; import org.thingsboard.server.service.telemetry.cmd.AttributesSubscriptionCmd;
import org.thingsboard.server.service.telemetry.cmd.GetHistoryCmd; import org.thingsboard.server.service.telemetry.cmd.GetHistoryCmd;
import org.thingsboard.server.service.telemetry.cmd.SubscriptionCmd; import org.thingsboard.server.service.telemetry.cmd.SubscriptionCmd;
@ -354,9 +355,9 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
}; };
if (StringUtils.isEmpty(cmd.getScope())) { if (StringUtils.isEmpty(cmd.getScope())) {
accessValidator.validate(sessionRef.getSecurityCtx(), entityId, getAttributesFetchCallback(sessionRef.getSecurityCtx().getTenantId(), entityId, keys, callback)); accessValidator.validate(sessionRef.getSecurityCtx(), Operation.READ_ATTRIBUTES, entityId, getAttributesFetchCallback(sessionRef.getSecurityCtx().getTenantId(), entityId, keys, callback));
} else { } else {
accessValidator.validate(sessionRef.getSecurityCtx(), entityId, getAttributesFetchCallback(sessionRef.getSecurityCtx().getTenantId(), entityId, cmd.getScope(), keys, callback)); accessValidator.validate(sessionRef.getSecurityCtx(), Operation.READ_ATTRIBUTES, entityId, getAttributesFetchCallback(sessionRef.getSecurityCtx().getTenantId(), entityId, cmd.getScope(), keys, callback));
} }
} }
@ -406,7 +407,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
sendWsMsg(sessionRef, update); sendWsMsg(sessionRef, update);
} }
}; };
accessValidator.validate(sessionRef.getSecurityCtx(), entityId, accessValidator.validate(sessionRef.getSecurityCtx(), Operation.READ_TELEMETRY, entityId,
on(r -> Futures.addCallback(tsService.findAll(sessionRef.getSecurityCtx().getTenantId(), entityId, queries), callback, executor), callback::onFailure)); on(r -> Futures.addCallback(tsService.findAll(sessionRef.getSecurityCtx().getTenantId(), entityId, queries), callback, executor), callback::onFailure));
} }
@ -436,9 +437,9 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
if (StringUtils.isEmpty(cmd.getScope())) { if (StringUtils.isEmpty(cmd.getScope())) {
accessValidator.validate(sessionRef.getSecurityCtx(), entityId, getAttributesFetchCallback(sessionRef.getSecurityCtx().getTenantId(), entityId, callback)); accessValidator.validate(sessionRef.getSecurityCtx(), Operation.READ_ATTRIBUTES, entityId, getAttributesFetchCallback(sessionRef.getSecurityCtx().getTenantId(), entityId, callback));
} else { } else {
accessValidator.validate(sessionRef.getSecurityCtx(), entityId, getAttributesFetchCallback(sessionRef.getSecurityCtx().getTenantId(), entityId, cmd.getScope(), callback)); accessValidator.validate(sessionRef.getSecurityCtx(), Operation.READ_ATTRIBUTES, entityId, getAttributesFetchCallback(sessionRef.getSecurityCtx().getTenantId(), entityId, cmd.getScope(), callback));
} }
} }
@ -474,14 +475,14 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
getLimit(cmd.getLimit()), getAggregation(cmd.getAgg()))).collect(Collectors.toList()); getLimit(cmd.getLimit()), getAggregation(cmd.getAgg()))).collect(Collectors.toList());
final FutureCallback<List<TsKvEntry>> callback = getSubscriptionCallback(sessionRef, cmd, sessionId, entityId, startTs, keys); final FutureCallback<List<TsKvEntry>> callback = getSubscriptionCallback(sessionRef, cmd, sessionId, entityId, startTs, keys);
accessValidator.validate(sessionRef.getSecurityCtx(), entityId, accessValidator.validate(sessionRef.getSecurityCtx(), Operation.READ_TELEMETRY, entityId,
on(r -> Futures.addCallback(tsService.findAll(sessionRef.getSecurityCtx().getTenantId(), entityId, queries), callback, executor), callback::onFailure)); on(r -> Futures.addCallback(tsService.findAll(sessionRef.getSecurityCtx().getTenantId(), entityId, queries), callback, executor), callback::onFailure));
} else { } else {
List<String> keys = new ArrayList<>(getKeys(cmd).orElse(Collections.emptySet())); List<String> keys = new ArrayList<>(getKeys(cmd).orElse(Collections.emptySet()));
startTs = System.currentTimeMillis(); startTs = System.currentTimeMillis();
log.debug("[{}] fetching latest timeseries data for keys: ({}) for device : {}", sessionId, cmd.getKeys(), entityId); log.debug("[{}] fetching latest timeseries data for keys: ({}) for device : {}", sessionId, cmd.getKeys(), entityId);
final FutureCallback<List<TsKvEntry>> callback = getSubscriptionCallback(sessionRef, cmd, sessionId, entityId, startTs, keys); final FutureCallback<List<TsKvEntry>> callback = getSubscriptionCallback(sessionRef, cmd, sessionId, entityId, startTs, keys);
accessValidator.validate(sessionRef.getSecurityCtx(), entityId, accessValidator.validate(sessionRef.getSecurityCtx(), Operation.READ_TELEMETRY, entityId,
on(r -> Futures.addCallback(tsService.findLatest(sessionRef.getSecurityCtx().getTenantId(), entityId, keys), callback, executor), callback::onFailure)); on(r -> Futures.addCallback(tsService.findLatest(sessionRef.getSecurityCtx().getTenantId(), entityId, keys), callback, executor), callback::onFailure));
} }
} }
@ -511,7 +512,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
sendWsMsg(sessionRef, update); sendWsMsg(sessionRef, update);
} }
}; };
accessValidator.validate(sessionRef.getSecurityCtx(), entityId, accessValidator.validate(sessionRef.getSecurityCtx(), Operation.READ_TELEMETRY, entityId,
on(r -> Futures.addCallback(tsService.findAllLatest(sessionRef.getSecurityCtx().getTenantId(), entityId), callback, executor), callback::onFailure)); on(r -> Futures.addCallback(tsService.findAllLatest(sessionRef.getSecurityCtx().getTenantId(), entityId), callback, executor), callback::onFailure));
} }

2
common/data/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java

@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import java.util.*; import java.util.*;
public class DashboardInfo extends SearchTextBased<DashboardId> implements HasName { public class DashboardInfo extends SearchTextBased<DashboardId> implements HasName, HasTenantId {
private TenantId tenantId; private TenantId tenantId;
private String title; private String title;

2
common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java

@ -19,5 +19,5 @@ package org.thingsboard.server.common.data;
* @author Andrew Shvayka * @author Andrew Shvayka
*/ */
public enum EntityType { public enum EntityType {
TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE
} }

9
common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.common.data; package org.thingsboard.server.common.data;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
@ -22,7 +23,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class Tenant extends ContactBased<TenantId> implements HasName { public class Tenant extends ContactBased<TenantId> implements HasName, HasTenantId {
private static final long serialVersionUID = 8057243243859922101L; private static final long serialVersionUID = 8057243243859922101L;
@ -51,6 +52,12 @@ public class Tenant extends ContactBased<TenantId> implements HasName {
this.title = title; this.title = title;
} }
@Override
@JsonIgnore
public TenantId getTenantId() {
return getId();
}
@Override @Override
@JsonProperty(access = JsonProperty.Access.READ_ONLY) @JsonProperty(access = JsonProperty.Access.READ_ONLY)
public String getName() { public String getName() {

4
common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java

@ -59,6 +59,10 @@ public class EntityIdFactory {
return new RuleNodeId(uuid); return new RuleNodeId(uuid);
case ENTITY_VIEW: case ENTITY_VIEW:
return new EntityViewId(uuid); return new EntityViewId(uuid);
case WIDGETS_BUNDLE:
return new WidgetsBundleId(uuid);
case WIDGET_TYPE:
return new WidgetTypeId(uuid);
} }
throw new IllegalArgumentException("EntityType " + type + " is not supported!"); throw new IllegalArgumentException("EntityType " + type + " is not supported!");
} }

9
common/data/src/main/java/org/thingsboard/server/common/data/id/WidgetTypeId.java

@ -18,9 +18,11 @@ package org.thingsboard.server.common.data.id;
import java.util.UUID; import java.util.UUID;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import org.thingsboard.server.common.data.EntityType;
public final class WidgetTypeId extends UUIDBased { public final class WidgetTypeId extends UUIDBased implements EntityId {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -29,4 +31,9 @@ public final class WidgetTypeId extends UUIDBased {
super(id); super(id);
} }
@JsonIgnore
@Override
public EntityType getEntityType() {
return EntityType.WIDGET_TYPE;
}
} }

9
common/data/src/main/java/org/thingsboard/server/common/data/id/WidgetsBundleId.java

@ -18,9 +18,11 @@ package org.thingsboard.server.common.data.id;
import java.util.UUID; import java.util.UUID;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import org.thingsboard.server.common.data.EntityType;
public final class WidgetsBundleId extends UUIDBased { public final class WidgetsBundleId extends UUIDBased implements EntityId {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -29,4 +31,9 @@ public final class WidgetsBundleId extends UUIDBased {
super(id); super(id);
} }
@JsonIgnore
@Override
public EntityType getEntityType() {
return EntityType.WIDGETS_BUNDLE;
}
} }

3
common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java

@ -18,11 +18,12 @@ package org.thingsboard.server.common.data.widget;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId; import org.thingsboard.server.common.data.id.WidgetTypeId;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class WidgetType extends BaseData<WidgetTypeId> { public class WidgetType extends BaseData<WidgetTypeId> implements HasTenantId {
private static final long serialVersionUID = 8388684344603660756L; private static final long serialVersionUID = 8388684344603660756L;

3
common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java

@ -15,13 +15,14 @@
*/ */
package org.thingsboard.server.common.data.widget; package org.thingsboard.server.common.data.widget;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.SearchTextBased; import org.thingsboard.server.common.data.SearchTextBased;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.id.WidgetsBundleId;
import java.util.Arrays; import java.util.Arrays;
public class WidgetsBundle extends SearchTextBased<WidgetsBundleId> { public class WidgetsBundle extends SearchTextBased<WidgetsBundleId> implements HasTenantId {
private static final long serialVersionUID = -7627368878362410489L; private static final long serialVersionUID = -7627368878362410489L;

2
common/message/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>common</artifactId> <artifactId>common</artifactId>

2
common/queue/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/transport/coap/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common.transport</groupId> <groupId>org.thingsboard.common.transport</groupId>

2
common/transport/http/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common.transport</groupId> <groupId>org.thingsboard.common.transport</groupId>

2
common/transport/mqtt/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common.transport</groupId> <groupId>org.thingsboard.common.transport</groupId>

2
common/transport/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/transport/transport-api/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common.transport</groupId> <groupId>org.thingsboard.common.transport</groupId>

2
dao/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>dao</artifactId> <artifactId>dao</artifactId>

91
dao/src/main/java/org/thingsboard/server/dao/model/sql/TsKvEntity.java

@ -62,49 +62,53 @@ public final class TsKvEntity implements ToData<TsKvEntry> {
} }
public TsKvEntity(Long longValue, Double doubleValue, Long longCountValue, Long doubleCountValue, String aggType) { public TsKvEntity(Long longValue, Double doubleValue, Long longCountValue, Long doubleCountValue, String aggType) {
switch (aggType) { if(!isAllNull(longValue, doubleValue, longCountValue, doubleCountValue)) {
case AVG: switch (aggType) {
double sum = 0.0; case AVG:
if (longValue != null) { double sum = 0.0;
sum += longValue; if (longValue != null) {
} sum += longValue;
if (doubleValue != null) { }
sum += doubleValue; if (doubleValue != null) {
} sum += doubleValue;
long totalCount = longCountValue + doubleCountValue; }
if (totalCount > 0) { long totalCount = longCountValue + doubleCountValue;
this.doubleValue = sum / (longCountValue + doubleCountValue); if (totalCount > 0) {
} else { this.doubleValue = sum / (longCountValue + doubleCountValue);
this.doubleValue = 0.0; } else {
} this.doubleValue = 0.0;
break; }
case SUM: break;
if (doubleCountValue > 0) { case SUM:
this.doubleValue = doubleValue + (longValue != null ? longValue.doubleValue() : 0.0); if (doubleCountValue > 0) {
} else { this.doubleValue = doubleValue + (longValue != null ? longValue.doubleValue() : 0.0);
this.longValue = longValue; } else {
} this.longValue = longValue;
break; }
case MIN: break;
case MAX: case MIN:
if (longCountValue > 0 && doubleCountValue > 0) { case MAX:
this.doubleValue = MAX.equals(aggType) ? Math.max(doubleValue, longValue.doubleValue()) : Math.min(doubleValue, longValue.doubleValue()); if (longCountValue > 0 && doubleCountValue > 0) {
} else if (doubleCountValue > 0) { this.doubleValue = MAX.equals(aggType) ? Math.max(doubleValue, longValue.doubleValue()) : Math.min(doubleValue, longValue.doubleValue());
this.doubleValue = doubleValue; } else if (doubleCountValue > 0) {
} else if (longCountValue > 0) { this.doubleValue = doubleValue;
this.longValue = longValue; } else if (longCountValue > 0) {
} this.longValue = longValue;
break; }
break;
}
} }
} }
public TsKvEntity(Long booleanValueCount, Long strValueCount, Long longValueCount, Long doubleValueCount) { public TsKvEntity(Long booleanValueCount, Long strValueCount, Long longValueCount, Long doubleValueCount) {
if (booleanValueCount != 0) { if(!isAllNull(booleanValueCount, strValueCount, longValueCount, doubleValueCount)) {
this.longValue = booleanValueCount; if (booleanValueCount != 0) {
} else if (strValueCount != 0) { this.longValue = booleanValueCount;
this.longValue = strValueCount; } else if (strValueCount != 0) {
} else { this.longValue = strValueCount;
this.longValue = longValueCount + doubleValueCount; } else {
this.longValue = longValueCount + doubleValueCount;
}
} }
} }
@ -155,4 +159,13 @@ public final class TsKvEntity implements ToData<TsKvEntry> {
public boolean isNotEmpty() { public boolean isNotEmpty() {
return strValue != null || longValue != null || doubleValue != null || booleanValue != null; return strValue != null || longValue != null || doubleValue != null || booleanValue != null;
} }
private static boolean isAllNull(Object... args) {
for (Object arg : args) {
if(arg != null) {
return false;
}
}
return true;
}
} }

18
dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java

@ -287,6 +287,20 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
return ruleNodes; return ruleNodes;
} }
@Override
public List<RuleNode> getReferencingRuleChainNodes(TenantId tenantId, RuleChainId ruleChainId) {
Validator.validateId(ruleChainId, "Incorrect rule chain id for search request.");
List<EntityRelation> relations = getNodeToRuleChainRelations(tenantId, ruleChainId);
List<RuleNode> ruleNodes = new ArrayList<>();
for (EntityRelation relation : relations) {
RuleNode ruleNode = ruleNodeDao.findById(tenantId, relation.getFrom().getId());
if (ruleNode != null) {
ruleNodes.add(ruleNode);
}
}
return ruleNodes;
}
@Override @Override
public List<EntityRelation> getRuleNodeRelations(TenantId tenantId, RuleNodeId ruleNodeId) { public List<EntityRelation> getRuleNodeRelations(TenantId tenantId, RuleNodeId ruleNodeId) {
Validator.validateId(ruleNodeId, "Incorrect rule node id for search request."); Validator.validateId(ruleNodeId, "Incorrect rule node id for search request.");
@ -351,6 +365,10 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
return relationService.findByFrom(tenantId, ruleChainId, RelationTypeGroup.RULE_CHAIN); return relationService.findByFrom(tenantId, ruleChainId, RelationTypeGroup.RULE_CHAIN);
} }
private List<EntityRelation> getNodeToRuleChainRelations(TenantId tenantId, RuleChainId ruleChainId) {
return relationService.findByTo(tenantId, ruleChainId, RelationTypeGroup.RULE_NODE);
}
private void deleteRuleNode(TenantId tenantId, EntityId entityId) { private void deleteRuleNode(TenantId tenantId, EntityId entityId) {
deleteEntityRelations(tenantId, entityId); deleteEntityRelations(tenantId, entityId);
ruleNodeDao.removeById(tenantId, entityId.getId()); ruleNodeDao.removeById(tenantId, entityId.getId());

2
dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java

@ -53,6 +53,8 @@ public interface RuleChainService {
List<RuleNode> getRuleChainNodes(TenantId tenantId, RuleChainId ruleChainId); List<RuleNode> getRuleChainNodes(TenantId tenantId, RuleChainId ruleChainId);
List<RuleNode> getReferencingRuleChainNodes(TenantId tenantId, RuleChainId ruleChainId);
List<EntityRelation> getRuleNodeRelations(TenantId tenantId, RuleNodeId ruleNodeId); List<EntityRelation> getRuleNodeRelations(TenantId tenantId, RuleNodeId ruleNodeId);
TextPageData<RuleChain> findTenantRuleChains(TenantId tenantId, TextPageLink pageLink); TextPageData<RuleChain> findTenantRuleChains(TenantId tenantId, TextPageLink pageLink);

3
dao/src/test/resources/application-test.properties

@ -27,9 +27,6 @@ caffeine.specs.assets.maxSize=100000
caffeine.specs.entityViews.timeToLiveInMinutes=1440 caffeine.specs.entityViews.timeToLiveInMinutes=1440
caffeine.specs.entityViews.maxSize=100000 caffeine.specs.entityViews.maxSize=100000
caching.specs.devices.timeToLiveInMinutes=1440
caching.specs.devices.maxSize=100000
redis.connection.host=localhost redis.connection.host=localhost
redis.connection.port=6379 redis.connection.port=6379
redis.connection.db=0 redis.connection.db=0

4
docker/docker-upgrade-tb.sh

@ -46,8 +46,6 @@ ADDITIONAL_STARTUP_SERVICES=$(additionalStartupServices) || exit $?
docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS pull tb1 docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS pull tb1
if [ ! -z "${ADDITIONAL_STARTUP_SERVICES// }" ]; then docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS up -d redis $ADDITIONAL_STARTUP_SERVICES
docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS up -d redis $ADDITIONAL_STARTUP_SERVICES
fi
docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS run --no-deps --rm -e UPGRADE_TB=true -e FROM_VERSION=${fromVersion} tb1 docker-compose -f docker-compose.yml $ADDITIONAL_COMPOSE_ARGS run --no-deps --rm -e UPGRADE_TB=true -e FROM_VERSION=${fromVersion} tb1

3
docker/tb-js-executor.env

@ -4,4 +4,5 @@ TB_KAFKA_SERVERS=kafka:9092
LOGGER_LEVEL=info LOGGER_LEVEL=info
LOG_FOLDER=logs LOG_FOLDER=logs
LOGGER_FILENAME=tb-js-executor-%DATE%.log LOGGER_FILENAME=tb-js-executor-%DATE%.log
DOCKER_MODE=true DOCKER_MODE=true
SCRIPT_BODY_TRACE_FREQUENCY=1000

2
msa/black-box-tests/pom.xml

@ -21,7 +21,7 @@
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

13
msa/js-executor/api/jsInvokeMessageProcessor.js

@ -15,16 +15,20 @@
*/ */
'use strict'; 'use strict';
const logger = require('../config/logger')('JsInvokeMessageProcessor'), const config = require('config'),
logger = require('../config/logger')('JsInvokeMessageProcessor'),
Utils = require('./utils'), Utils = require('./utils'),
js = require('./jsinvoke.proto').js, js = require('./jsinvoke.proto').js,
KeyedMessage = require('kafka-node').KeyedMessage, KeyedMessage = require('kafka-node').KeyedMessage,
JsExecutor = require('./jsExecutor'); JsExecutor = require('./jsExecutor');
const scriptBodyTraceFrequency = Number(config.get('script.script_body_trace_frequency'));
function JsInvokeMessageProcessor(producer) { function JsInvokeMessageProcessor(producer) {
this.producer = producer; this.producer = producer;
this.executor = new JsExecutor(); this.executor = new JsExecutor();
this.scriptMap = {}; this.scriptMap = {};
this.executedScriptsCounter = 0;
} }
JsInvokeMessageProcessor.prototype.onJsInvokeMessage = function(message) { JsInvokeMessageProcessor.prototype.onJsInvokeMessage = function(message) {
@ -74,6 +78,13 @@ JsInvokeMessageProcessor.prototype.processCompileRequest = function(requestId, r
JsInvokeMessageProcessor.prototype.processInvokeRequest = function(requestId, responseTopic, invokeRequest) { JsInvokeMessageProcessor.prototype.processInvokeRequest = function(requestId, responseTopic, invokeRequest) {
var scriptId = getScriptId(invokeRequest); var scriptId = getScriptId(invokeRequest);
logger.debug('[%s] Processing invoke request, scriptId: [%s]', requestId, scriptId); logger.debug('[%s] Processing invoke request, scriptId: [%s]', requestId, scriptId);
this.executedScriptsCounter++;
if ( this.executedScriptsCounter >= scriptBodyTraceFrequency ) {
this.executedScriptsCounter = 0;
if (logger.levels[logger.level] >= logger.levels['debug']) {
logger.debug('[%s] Executing script body: [%s]', scriptId, invokeRequest.scriptBody);
}
}
this.getOrCompileScript(scriptId, invokeRequest.scriptBody).then( this.getOrCompileScript(scriptId, invokeRequest.scriptBody).then(
(script) => { (script) => {
this.executor.executeScript(script, invokeRequest.args, invokeRequest.timeout).then( this.executor.executeScript(script, invokeRequest.args, invokeRequest.timeout).then(

3
msa/js-executor/config/custom-environment-variables.yml

@ -23,3 +23,6 @@ logger:
level: "LOGGER_LEVEL" level: "LOGGER_LEVEL"
path: "LOG_FOLDER" path: "LOG_FOLDER"
filename: "LOGGER_FILENAME" filename: "LOGGER_FILENAME"
script:
script_body_trace_frequency: "SCRIPT_BODY_TRACE_FREQUENCY"

3
msa/js-executor/config/default.yml

@ -24,3 +24,6 @@ logger:
level: "info" level: "info"
path: "logs" path: "logs"
filename: "tb-js-executor-%DATE%.log" filename: "tb-js-executor-%DATE%.log"
script:
script_body_trace_frequency: "1000"

2
msa/js-executor/package-lock.json

@ -1,6 +1,6 @@
{ {
"name": "thingsboard-js-executor", "name": "thingsboard-js-executor",
"version": "2.2.1", "version": "2.3.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

2
msa/js-executor/package.json

@ -1,7 +1,7 @@
{ {
"name": "thingsboard-js-executor", "name": "thingsboard-js-executor",
"private": true, "private": true,
"version": "2.2.1", "version": "2.3.1",
"description": "ThingsBoard JavaScript Executor Microservice", "description": "ThingsBoard JavaScript Executor Microservice",
"main": "server.js", "main": "server.js",
"bin": "server.js", "bin": "server.js",

2
msa/js-executor/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

2
msa/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>

2
msa/tb-node/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

4
msa/tb/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>
@ -40,7 +40,7 @@
<tb-cassandra.docker.name>tb-cassandra</tb-cassandra.docker.name> <tb-cassandra.docker.name>tb-cassandra</tb-cassandra.docker.name>
<pkg.user>thingsboard</pkg.user> <pkg.user>thingsboard</pkg.user>
<pkg.installFolder>/usr/share/${pkg.name}</pkg.installFolder> <pkg.installFolder>/usr/share/${pkg.name}</pkg.installFolder>
<pkg.upgradeVersion>2.1.3</pkg.upgradeVersion> <pkg.upgradeVersion>2.2.0</pkg.upgradeVersion>
</properties> </properties>
<dependencies> <dependencies>

2
msa/transport/coap/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa.transport</groupId> <groupId>org.thingsboard.msa.transport</groupId>

2
msa/transport/http/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa.transport</groupId> <groupId>org.thingsboard.msa.transport</groupId>

2
msa/transport/mqtt/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa.transport</groupId> <groupId>org.thingsboard.msa.transport</groupId>

2
msa/transport/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

2
msa/web-ui/package-lock.json

@ -1,6 +1,6 @@
{ {
"name": "thingsboard-web-ui", "name": "thingsboard-web-ui",
"version": "2.2.1", "version": "2.3.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

2
msa/web-ui/package.json

@ -1,7 +1,7 @@
{ {
"name": "thingsboard-web-ui", "name": "thingsboard-web-ui",
"private": true, "private": true,
"version": "2.2.1", "version": "2.3.1",
"description": "ThingsBoard Web UI Microservice", "description": "ThingsBoard Web UI Microservice",
"main": "server.js", "main": "server.js",
"bin": "server.js", "bin": "server.js",

2
msa/web-ui/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

4
netty-mqtt/pom.xml

@ -19,12 +19,12 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<artifactId>netty-mqtt</artifactId> <artifactId>netty-mqtt</artifactId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>Netty MQTT Client</name> <name>Netty MQTT Client</name>

2
pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>Thingsboard</name> <name>Thingsboard</name>

2
rule-engine/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>rule-engine</artifactId> <artifactId>rule-engine</artifactId>

2
rule-engine/rule-engine-api/pom.xml

@ -22,7 +22,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>rule-engine</artifactId> <artifactId>rule-engine</artifactId>
</parent> </parent>
<groupId>org.thingsboard.rule-engine</groupId> <groupId>org.thingsboard.rule-engine</groupId>

2
rule-engine/rule-engine-components/pom.xml

@ -22,7 +22,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>rule-engine</artifactId> <artifactId>rule-engine</artifactId>
</parent> </parent>
<groupId>org.thingsboard.rule-engine</groupId> <groupId>org.thingsboard.rule-engine</groupId>

12
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java

@ -71,10 +71,14 @@ public class TbRestApiCallNode implements TbNode {
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
try { try {
this.config = TbNodeUtils.convert(configuration, TbRestApiCallNodeConfiguration.class); this.config = TbNodeUtils.convert(configuration, TbRestApiCallNodeConfiguration.class);
this.eventLoopGroup = new NioEventLoopGroup(); if (this.config.isUseSimpleClientHttpFactory()) {
Netty4ClientHttpRequestFactory nettyFactory = new Netty4ClientHttpRequestFactory(this.eventLoopGroup); httpClient = new AsyncRestTemplate();
nettyFactory.setSslContext(SslContextBuilder.forClient().build()); } else {
httpClient = new AsyncRestTemplate(nettyFactory); this.eventLoopGroup = new NioEventLoopGroup();
Netty4ClientHttpRequestFactory nettyFactory = new Netty4ClientHttpRequestFactory(this.eventLoopGroup);
nettyFactory.setSslContext(SslContextBuilder.forClient().build());
httpClient = new AsyncRestTemplate(nettyFactory);
}
} catch (SSLException e) { } catch (SSLException e) {
throw new TbNodeException(e); throw new TbNodeException(e);
} }

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java

@ -27,6 +27,7 @@ public class TbRestApiCallNodeConfiguration implements NodeConfiguration<TbRestA
private String restEndpointUrlPattern; private String restEndpointUrlPattern;
private String requestMethod; private String requestMethod;
private Map<String, String> headers; private Map<String, String> headers;
private boolean useSimpleClientHttpFactory;
@Override @Override
public TbRestApiCallNodeConfiguration defaultConfiguration() { public TbRestApiCallNodeConfiguration defaultConfiguration() {
@ -34,6 +35,7 @@ public class TbRestApiCallNodeConfiguration implements NodeConfiguration<TbRestA
configuration.setRestEndpointUrlPattern("http://localhost/api"); configuration.setRestEndpointUrlPattern("http://localhost/api");
configuration.setRequestMethod("POST"); configuration.setRequestMethod("POST");
configuration.setHeaders(Collections.emptyMap()); configuration.setHeaders(Collections.emptyMap());
configuration.setUseSimpleClientHttpFactory(false);
return configuration; return configuration;
} }
} }

6
rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js

File diff suppressed because one or more lines are too long

2
tools/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>tools</artifactId> <artifactId>tools</artifactId>

2
transport/coap/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.transport</groupId> <groupId>org.thingsboard.transport</groupId>

2
transport/http/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.transport</groupId> <groupId>org.thingsboard.transport</groupId>

2
transport/mqtt/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.transport</groupId> <groupId>org.thingsboard.transport</groupId>

2
transport/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>

2
ui/package-lock.json

@ -1,6 +1,6 @@
{ {
"name": "thingsboard", "name": "thingsboard",
"version": "2.2.1", "version": "2.3.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

2
ui/package.json

@ -1,7 +1,7 @@
{ {
"name": "thingsboard", "name": "thingsboard",
"private": true, "private": true,
"version": "2.2.1", "version": "2.3.1",
"description": "ThingsBoard UI", "description": "ThingsBoard UI",
"licenses": [ "licenses": [
{ {

2
ui/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>2.2.1-SNAPSHOT</version> <version>2.3.1-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>

1
ui/src/app/locale/locale.constant-de_DE.json

@ -1559,6 +1559,7 @@
"widget-action": { "widget-action": {
"action-cell-button": "Aktionszellenschaltfläche", "action-cell-button": "Aktionszellenschaltfläche",
"row-click": "Klick auf Zeile", "row-click": "Klick auf Zeile",
"polygon-click": "Klick auf Polygon",
"marker-click": "Klick auf Marker", "marker-click": "Klick auf Marker",
"tooltip-tag-action": "Tooltip-Tag-Aktion" "tooltip-tag-action": "Tooltip-Tag-Aktion"
} }

1
ui/src/app/locale/locale.constant-en_US.json

@ -1564,6 +1564,7 @@
"widget-action": { "widget-action": {
"action-cell-button": "Action cell button", "action-cell-button": "Action cell button",
"row-click": "On row click", "row-click": "On row click",
"polygon-click": "On polygon click",
"marker-click": "On marker click", "marker-click": "On marker click",
"tooltip-tag-action": "Tooltip tag action" "tooltip-tag-action": "Tooltip tag action"
} }

3
ui/src/app/locale/locale.constant-es_ES.json

@ -1559,7 +1559,8 @@
"widget-action": { "widget-action": {
"action-cell-button": "Botón de acción de celda", "action-cell-button": "Botón de acción de celda",
"row-click": "Clic en la fila", "row-click": "Clic en la fila",
"marker-click": "Clic en el marcador", "polygon-click": "Clic en la fila",
"marker-click": "Clic en el polígono",
"tooltip-tag-action": "Acción de etiqueta para globo de ayuda" "tooltip-tag-action": "Acción de etiqueta para globo de ayuda"
} }
}, },

1
ui/src/app/locale/locale.constant-fr_FR.json

@ -336,6 +336,7 @@
"action-cell-button": "Action cell button", "action-cell-button": "Action cell button",
"marker-click": "On marker click", "marker-click": "On marker click",
"row-click": "On row click", "row-click": "On row click",
"polygon-click": "On polygon click",
"tooltip-tag-action": "Tooltip tag action" "tooltip-tag-action": "Tooltip tag action"
} }
}, },

304
ui/src/app/locale/locale.constant-it_IT.json

@ -133,8 +133,13 @@
"min-polling-interval-message": "L'intervallo di polling deve essere di almeno 1 sec.", "min-polling-interval-message": "L'intervallo di polling deve essere di almeno 1 sec.",
"aknowledge-alarms-title": "Conferma { count, plural, 1 {1 allarme} other {# allarmi} }", "aknowledge-alarms-title": "Conferma { count, plural, 1 {1 allarme} other {# allarmi} }",
"aknowledge-alarms-text": "Sei sicuro di voler confermare { count, plural, 1 {1 allarme} other {# allarmi} }?", "aknowledge-alarms-text": "Sei sicuro di voler confermare { count, plural, 1 {1 allarme} other {# allarmi} }?",
"aknowledge-alarm-title": "Conferma allarme",
"aknowledge-alarm-text": "Sei sicuro di voler confermare l'allarme?",
"clear-alarms-title": "Elimina { count, plural, 1 {1 allarme} other {# allarmi} }", "clear-alarms-title": "Elimina { count, plural, 1 {1 allarme} other {# allarmi} }",
"clear-alarms-text": "Sei sicuro di voler eliminare { count, plural, 1 {1 allarme} other {# allarmi} }?" "clear-alarms-text": "Sei sicuro di voler eliminare { count, plural, 1 {1 allarme} other {# allarmi} }?",
"clear-alarm-title": "Elimina allarme",
"clear-alarm-text": "Sei sicuro di voler eliminare l'allarme?",
"alarm-status-filter": "Filtro stato allarme"
}, },
"alias": { "alias": {
"add": "Aggiungi alias", "add": "Aggiungi alias",
@ -145,7 +150,7 @@
"filter-type-single-entity": "Singola entità", "filter-type-single-entity": "Singola entità",
"filter-type-entity-list": "Lista Entità", "filter-type-entity-list": "Lista Entità",
"filter-type-entity-name": "Nome Entità", "filter-type-entity-name": "Nome Entità",
"filter-type-state-entity": "Entity from dashboard state", "filter-type-state-entity": "Entità dallo stato della dashboard",
"filter-type-state-entity-description": "Entità prelevata dai parametri di stato della dashboard", "filter-type-state-entity-description": "Entità prelevata dai parametri di stato della dashboard",
"filter-type-asset-type": "Tipo di Asset", "filter-type-asset-type": "Tipo di Asset",
"filter-type-asset-type-description": "Asset di tipo '{{assetType}}'", "filter-type-asset-type-description": "Asset di tipo '{{assetType}}'",
@ -153,26 +158,31 @@
"filter-type-device-type": "Tipo di dispositivo", "filter-type-device-type": "Tipo di dispositivo",
"filter-type-device-type-description": "Dispositivi di tipo '{{deviceType}}'", "filter-type-device-type-description": "Dispositivi di tipo '{{deviceType}}'",
"filter-type-device-type-and-name-description": "Dispositivi di tipo '{{deviceType}}' e con un nome che inizia per '{{prefix}}'", "filter-type-device-type-and-name-description": "Dispositivi di tipo '{{deviceType}}' e con un nome che inizia per '{{prefix}}'",
"filter-type-relations-query": "Relations query", "filter-type-entity-view-type": "Tipo vista entità",
"filter-type-relations-query-description": "{{entities}} that have {{relationType}} relation {{direction}} {{rootEntity}}", "filter-type-entity-view-type-description": "Viste entità di tipo '{{entityView}}'",
"filter-type-entity-view-type-and-name-description": "Viste entità di tipo '{{entityView}}' e con un nome che inizia per '{{prefix}}'",
"filter-type-relations-query": "Query relazioni",
"filter-type-relations-query-description": "{{entities}} che hanno una relazione {{relationType}} {{direction}} {{rootEntity}}",
"filter-type-asset-search-query": "Query ricerca asset", "filter-type-asset-search-query": "Query ricerca asset",
"filter-type-asset-search-query-description": "Assets with types {{assetTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}", "filter-type-asset-search-query-description": "Asset di tipo {{assetTypes}} che hanno una relazione {{relationType}} {{direction}} {{rootEntity}}",
"filter-type-device-search-query": "Query ricerca dispositivo", "filter-type-device-search-query": "Query ricerca dispositivo",
"filter-type-device-search-query-description": "Devices with types {{deviceTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}", "filter-type-device-search-query-description": "Dispositivi di tipo {{deviceTypes}} che hanno una relazione {{relationType}} {{direction}} {{rootEntity}}",
"filter-type-entity-view-search-query": "Query ricerca Vista entità",
"filter-type-entity-view-search-query-description": "Viste entità di tipo {{entityViewTypes}} che hanno una relazione {{relationType}} {{direction}} {{rootEntity}}",
"entity-filter": "Filtro entità", "entity-filter": "Filtro entità",
"resolve-multiple": "Resolve as multiple entities", "resolve-multiple": "Risolvi come entità multiple",
"filter-type": "Tipo di filtro", "filter-type": "Tipo di filtro",
"filter-type-required": "Tipo di filtro richiesto.", "filter-type-required": "Tipo di filtro richiesto.",
"entity-filter-no-entity-matched": "Nessuna entità corrispondente al filtro specificato è stata trovata.", "entity-filter-no-entity-matched": "Nessuna entità corrispondente al filtro specificato è stata trovata.",
"no-entity-filter-specified": "Nessun filtro di entità specificato", "no-entity-filter-specified": "Nessun filtro di entità specificato",
"root-state-entity": "Use dashboard state entity as root", "root-state-entity": "Usa l'entità di stato della dashboard come radice",
"root-entity": "Entità radice", "root-entity": "Entità radice",
"state-entity-parameter-name": "State entity parameter name", "state-entity-parameter-name": "Nome parametro entità di stato",
"default-state-entity": "Default state entity", "default-state-entity": "Entità di stato predefinita",
"default-entity-parameter-name": "By default", "default-entity-parameter-name": "Predefinito",
"max-relation-level": "Max relation level", "max-relation-level": "Massimo livello relazione",
"unlimited-level": "Unlimited level", "unlimited-level": "Illimitato",
"state-entity": "Dashboard state entity", "state-entity": "Entità di stato della dashboard",
"all-entities": "Tutte le entità", "all-entities": "Tutte le entità",
"any-relation": "qualsiasi" "any-relation": "qualsiasi"
}, },
@ -212,10 +222,10 @@
"add-asset-text": "Aggiungi un nuovo asset", "add-asset-text": "Aggiungi un nuovo asset",
"asset-details": "Dettagli Asset", "asset-details": "Dettagli Asset",
"assign-assets": "Assegna asset", "assign-assets": "Assegna asset",
"assign-assets-text": "Assegna { count, plural, 1 {1 asset} other {# assets} } al cliente", "assign-assets-text": "Assegna { count, plural, 1 {1 asset} other {# asset} } al cliente",
"delete-assets": "Cancella asset", "delete-assets": "Cancella asset",
"unassign-assets": "Annulla assegnazione asset", "unassign-assets": "Annulla assegnazione asset",
"unassign-assets-action-title": "Unassign { count, plural, 1 {1 asset} other {# assets} } from customer", "unassign-assets-action-title": "Annulla assegnazione { count, plural, 1 {1 asset} other {# asset} } al cliente",
"assign-new-asset": "Assegna un nuovo asset", "assign-new-asset": "Assegna un nuovo asset",
"delete-asset-title": "Sei sicuro di voler cancellare l'asset '{{assetName}}'?", "delete-asset-title": "Sei sicuro di voler cancellare l'asset '{{assetName}}'?",
"delete-asset-text": "Attenzione, dopo la conferma l'asset e tutti i relativi dati non saranno più recuperabili.", "delete-asset-text": "Attenzione, dopo la conferma l'asset e tutti i relativi dati non saranno più recuperabili.",
@ -234,7 +244,7 @@
"copyId": "Copia Id asset", "copyId": "Copia Id asset",
"idCopiedMessage": "Id Asset copiato negli Appunti", "idCopiedMessage": "Id Asset copiato negli Appunti",
"select-asset": "Seleziona asset", "select-asset": "Seleziona asset",
"no-assets-matching": "Nessun asset corrispondente a '{{entity}}' é stato trovato.", "no-assets-matching": "Nessun asset corrispondente a '{{entity}}' è stato trovato.",
"asset-required": "Asset obbligatorio", "asset-required": "Asset obbligatorio",
"name-starts-with": "Asset con nome che inizia per" "name-starts-with": "Asset con nome che inizia per"
}, },
@ -288,12 +298,17 @@
"type-suspended": "Sospeso", "type-suspended": "Sospeso",
"type-credentials-read": "Credenziali lette", "type-credentials-read": "Credenziali lette",
"type-attributes-read": "Attributi letti", "type-attributes-read": "Attributi letti",
"status-success": "Success", "type-relation-add-or-update": "Relazione aggiornata",
"status-failure": "Failure", "type-relation-delete": "Relazione eliminata",
"type-relations-delete": "Eliminate tutte le relazioni",
"type-alarm-ack": "Confermato",
"type-alarm-clear": "Eliminato",
"status-success": "Successo",
"status-failure": "Fallito",
"audit-log-details": "Dettaglio log audit", "audit-log-details": "Dettaglio log audit",
"no-audit-logs-prompt": "Log non trovati", "no-audit-logs-prompt": "Log non trovati",
"action-data": "Action data", "action-data": "Action data",
"failure-details": "Failure details", "failure-details": "Dettagli fallimento",
"search": "Cerca log audit", "search": "Cerca log audit",
"clear-search": "Cancella ricerca" "clear-search": "Cancella ricerca"
}, },
@ -333,10 +348,12 @@
"dashboard": "Dashboard cliente", "dashboard": "Dashboard cliente",
"dashboards": "Dashboard cliente", "dashboards": "Dashboard cliente",
"devices": "Dispositivi cliente", "devices": "Dispositivi cliente",
"entity-views": "Viste entità cliente",
"assets": "Asset cliente", "assets": "Asset cliente",
"public-dashboards": "Dashboard pubbliche", "public-dashboards": "Dashboard pubbliche",
"public-devices": "Dispositivi pubblici", "public-devices": "Dispositivi pubblici",
"public-assets": "Asset pubblici", "public-assets": "Asset pubblici",
"public-entity-views": "Viste entità pubbliche",
"add": "Aggiungi cliente", "add": "Aggiungi cliente",
"delete": "Elimina cliente", "delete": "Elimina cliente",
"manage-customer-users": "Gestisci utenti cliente", "manage-customer-users": "Gestisci utenti cliente",
@ -388,14 +405,14 @@
"assign-dashboard-to-customer-text": "Seleziona le dashboard da assegnare al client", "assign-dashboard-to-customer-text": "Seleziona le dashboard da assegnare al client",
"assign-to-customer-text": "Seleziona il cliente a cui assegnare la/le dashboard", "assign-to-customer-text": "Seleziona il cliente a cui assegnare la/le dashboard",
"assign-to-customer": "Assegna al cliente", "assign-to-customer": "Assegna al cliente",
"unassign-from-customer": "Unassign from customer", "unassign-from-customer": "Annulla assegnazione al cliente",
"make-public": "Rendi pubblica la dashboard", "make-public": "Rendi pubblica la dashboard",
"make-private": "Rendi privata la dashboard", "make-private": "Rendi privata la dashboard",
"manage-assigned-customers": "Gestisci clienti assegnati", "manage-assigned-customers": "Gestisci clienti assegnati",
"assigned-customers": "Clienti assegnati", "assigned-customers": "Clienti assegnati",
"assign-to-customers": "Assegna Dashboard ai Clienti", "assign-to-customers": "Assegna Dashboard ai Clienti",
"assign-to-customers-text": "Seleziona i clienti da assegnare alla/alle dashboard", "assign-to-customers-text": "Seleziona i clienti da assegnare alla/alle dashboard",
"unassign-from-customers": "Unassign Dashboard(s) From Customers", "unassign-from-customers": "Annulla assegnazione Dashboard ai Clienti",
"unassign-from-customers-text": "Seleziona i clienti di cui annullare l'assegnazione alla/alle dashboard", "unassign-from-customers-text": "Seleziona i clienti di cui annullare l'assegnazione alla/alle dashboard",
"no-dashboards-text": "Nessuna dashboard trovata", "no-dashboards-text": "Nessuna dashboard trovata",
"no-widgets": "Nessun widget configurato", "no-widgets": "Nessun widget configurato",
@ -412,10 +429,10 @@
"assign-dashboards": "Assegna dashboard", "assign-dashboards": "Assegna dashboard",
"assign-new-dashboard": "Assegna nuova dashboard", "assign-new-dashboard": "Assegna nuova dashboard",
"assign-dashboards-text": "Assegna { count, plural, 1 {1 dashboard} other {# dashboard} } ai clienti", "assign-dashboards-text": "Assegna { count, plural, 1 {1 dashboard} other {# dashboard} } ai clienti",
"unassign-dashboards-action-text": "Annulla assegnazione { count, plural, 1 {1 dashboard} other {# dashboards} } ai clienti", "unassign-dashboards-action-text": "Annulla assegnazione { count, plural, 1 {1 dashboard} other {# dashboard} } ai clienti",
"delete-dashboards": "Elimina dashboard", "delete-dashboards": "Elimina dashboard",
"unassign-dashboards": "Annulla assegnazione dashboard", "unassign-dashboards": "Annulla assegnazione dashboard",
"unassign-dashboards-action-title": "Annulla assegnazione { count, plural, 1 {1 dashboard} other {# dashboards} } al cliente", "unassign-dashboards-action-title": "Annulla assegnazione { count, plural, 1 {1 dashboard} other {# dashboard} } al cliente",
"delete-dashboard-title": "Sei sicuro di voler cancellare la dashboard '{{dashboardTitle}}'?", "delete-dashboard-title": "Sei sicuro di voler cancellare la dashboard '{{dashboardTitle}}'?",
"delete-dashboard-text": "Attenzione, dopo la conferma la dashboard e tutti i suoi dati non saranno più recuperabili.", "delete-dashboard-text": "Attenzione, dopo la conferma la dashboard e tutti i suoi dati non saranno più recuperabili.",
"delete-dashboards-title": "Sei sicuro di voler eliminare { count, plural, 1 {1 dashboard} other {# dashboard} }?", "delete-dashboards-title": "Sei sicuro di voler eliminare { count, plural, 1 {1 dashboard} other {# dashboard} }?",
@ -425,7 +442,7 @@
"unassign-dashboard-text": "Dopo la conferma sarà annullata l'assegnazione della dashboard e questa non sarà più accessibile dal cliente.", "unassign-dashboard-text": "Dopo la conferma sarà annullata l'assegnazione della dashboard e questa non sarà più accessibile dal cliente.",
"unassign-dashboard": "Annulla assegnazione dashboard", "unassign-dashboard": "Annulla assegnazione dashboard",
"unassign-dashboards-title": "Sei sicuro di voler annullare l'assegnazione di { count, plural, 1 {1 dashboard} other {# dashboard} }?", "unassign-dashboards-title": "Sei sicuro di voler annullare l'assegnazione di { count, plural, 1 {1 dashboard} other {# dashboard} }?",
"unassign-dashboards-text": "Dopo la conferma sarà annullata l'assegnazione di tutte le dashboards selezionate e queste non saranno più accessibili dal cliente.", "unassign-dashboards-text": "Dopo la conferma sarà annullata l'assegnazione di tutte le dashboard selezionate e queste non saranno più accessibili dal cliente.",
"public-dashboard-title": "La Dashboard è ora pubblica", "public-dashboard-title": "La Dashboard è ora pubblica",
"public-dashboard-text": "La dashboard <b>{{dashboardTitle}}</b> è ora pubblica e accessibile al <a href='{{publicLink}}' target='_blank'>link</a>:", "public-dashboard-text": "La dashboard <b>{{dashboardTitle}}</b> è ora pubblica e accessibile al <a href='{{publicLink}}' target='_blank'>link</a>:",
"public-dashboard-notice": "<b>Nota:</b> Ricorda di rendere pubblici i relativi dispositivi per accedere ai loro dati.", "public-dashboard-notice": "<b>Nota:</b> Ricorda di rendere pubblici i relativi dispositivi per accedere ai loro dati.",
@ -461,12 +478,12 @@
"vertical-margin-required": "Margine verticale obbligatorio.", "vertical-margin-required": "Margine verticale obbligatorio.",
"min-vertical-margin-message": "Ammesso un margine verticale minimo pari a 0.", "min-vertical-margin-message": "Ammesso un margine verticale minimo pari a 0.",
"max-vertical-margin-message": "Ammesso un margine verticale massimo pari a 50.", "max-vertical-margin-message": "Ammesso un margine verticale massimo pari a 50.",
"autofill-height": "Auto fill layout height", "autofill-height": "Riempi automaticamente altezza layout",
"mobile-layout": "Impostazioni layout mobile", "mobile-layout": "Impostazioni layout mobile",
"mobile-row-height": "Mobile row height, px", "mobile-row-height": "Altezza riga mobile (px)",
"mobile-row-height-required": "Mobile row height value is required.", "mobile-row-height-required": "Altezza riga mobile è richiesta.",
"min-mobile-row-height-message": "Only 5 pixels is allowed as minimum mobile row height value.", "min-mobile-row-height-message": "5 pixel è il minimo concesso al valore altezza riga mobile.",
"max-mobile-row-height-message": "Only 200 pixels is allowed as maximum mobile row height value.", "max-mobile-row-height-message": "200 pixel è il massimo concesso al valore altezza riga mobile.",
"display-title": "Mostra titolo dashboard", "display-title": "Mostra titolo dashboard",
"toolbar-always-open": "Mantieni aperta la barra degli strumenti", "toolbar-always-open": "Mantieni aperta la barra degli strumenti",
"title-color": "Colore titolo", "title-color": "Colore titolo",
@ -527,18 +544,23 @@
"units": "Simbolo speciale da mostrare accanto al valore", "units": "Simbolo speciale da mostrare accanto al valore",
"decimals": "Numero cifre decimali", "decimals": "Numero cifre decimali",
"data-generation-func": "Funzione generazione dati", "data-generation-func": "Funzione generazione dati",
"use-data-post-processing-func": "Use data post-processing function", "use-data-post-processing-func": "Usa funzione dopo il processamento dei dati",
"configuration": "Configurazione data key", "configuration": "Configurazione data key",
"timeseries": "Serie temporali", "timeseries": "Serie temporali",
"attributes": "Attributi", "attributes": "Attributi",
"alarm": "Campi allarme", "alarm": "Campi allarme",
"timeseries-required": "Entity timeseries are required.", "timeseries-required": "Le serie temporali dell'entità sono richieste.",
"timeseries-or-attributes-required": "Entity timeseries/attributes are required.", "timeseries-or-attributes-required": "Le serie temporali o gli attributi dell'entità sono richiesti.",
"maximum-timeseries-or-attributes": "Massimo { count, plural, 1 {1 serie temporale/attributo consentito.} other {# serie temporali/attributi consentiti.} }", "maximum-timeseries-or-attributes": "Massimo { count, plural, 1 {1 serie temporale/attributo consentito.} other {# serie temporali/attributi consentiti.} }",
"alarm-fields-required": "Campi allarme obbligatori.", "alarm-fields-required": "Campi allarme obbligatori.",
"function-types": "Tipi funzione", "function-types": "Tipi funzione",
"function-types-required": "Tipi funzione obbligatorio.", "function-types-required": "Tipi funzione obbligatorio.",
"maximum-function-types": "Massimo { count, plural, 1 {1 tipo di funzione consentito.} other {# tipi di funzione consentiti} }" "maximum-function-types": "Massimo { count, plural, 1 {1 tipo di funzione consentito.} other {# tipi di funzione consentiti} }",
"time-description": "timestamp del valore corrente;",
"value-description": "il valore corrente;",
"prev-value-description": "risultato della precedente chiamata alla funzione;",
"time-prev-description": "timestamp del valore precedente;",
"prev-orig-value-description": "valore precedente originale;"
}, },
"datasource": { "datasource": {
"type": "Tipo sorgente dati", "type": "Tipo sorgente dati",
@ -563,11 +585,11 @@
"no-keys-found": "Nessuna chiave trovata.", "no-keys-found": "Nessuna chiave trovata.",
"create-new-alias": "Creane uno nuovo!", "create-new-alias": "Creane uno nuovo!",
"create-new-key": "Creane una nuova!", "create-new-key": "Creane una nuova!",
"duplicate-alias-error": "Sono stati trovati dei duplicati dell'alias '{{alias}}'.<br>Gli alias di un dispositivo devono essere univoci all'interno della dashboard.", "duplicate-alias-error": "Sono stati trovati dei duplicati dell'alias '{{alias}}'.<br/>Gli alias di un dispositivo devono essere univoci all'interno della dashboard.",
"configure-alias": "Configura alias '{{alias}}'", "configure-alias": "Configura alias '{{alias}}'",
"no-devices-matching": "Nessun dispositivo corrispondente a '{{entity}}' é stato trovato.", "no-devices-matching": "Nessun dispositivo corrispondente a '{{entity}}' è stato trovato.",
"alias": "Alias", "alias": "Alias",
"alias-required": "Alias dispositivo richesto.", "alias-required": "Alias dispositivo richiesto.",
"remove-alias": "Rimuovi alias dispositivo", "remove-alias": "Rimuovi alias dispositivo",
"add-alias": "Aggiungi alias dispositivo", "add-alias": "Aggiungi alias dispositivo",
"name-starts-with": "Dispositivo il cui nome inizia per", "name-starts-with": "Dispositivo il cui nome inizia per",
@ -639,8 +661,8 @@
"accessTokenCopiedMessage": "Token di accesso del dispositivo copiato negli Appunti", "accessTokenCopiedMessage": "Token di accesso del dispositivo copiato negli Appunti",
"assignedToCustomer": "Assegnato al cliente", "assignedToCustomer": "Assegnato al cliente",
"unable-delete-device-alias-title": "Impossibile rimuovere l'alias del dispositivo", "unable-delete-device-alias-title": "Impossibile rimuovere l'alias del dispositivo",
"unable-delete-device-alias-text": "L'alias del dispositivo '{{deviceAlias}}' non può essere eliminato perchè utilizzato dai seguenti widget:<br/>{{widgetsList}}", "unable-delete-device-alias-text": "L'alias del dispositivo '{{deviceAlias}}' non può essere eliminato perché utilizzato dai seguenti widget:<br/>{{widgetsList}}",
"is-gateway": "E' un gateway", "is-gateway": "È un gateway",
"public": "Pubblico", "public": "Pubblico",
"device-public": "Il dispositivo è pubblico", "device-public": "Il dispositivo è pubblico",
"select-device": "Seleziona dispositivo" "select-device": "Seleziona dispositivo"
@ -659,9 +681,9 @@
"aliases": "Alias entità", "aliases": "Alias entità",
"entity-alias": "Alias entità", "entity-alias": "Alias entità",
"unable-delete-entity-alias-title": "Impossibile eliminare alias entità", "unable-delete-entity-alias-title": "Impossibile eliminare alias entità",
"unable-delete-entity-alias-text": "L'alias dell'entità '{{entityAlias}}' non può essere eliminato perchè utilizzato dai seguenti widget:<br/>{{widgetsList}}", "unable-delete-entity-alias-text": "L'alias dell'entità '{{entityAlias}}' non può essere eliminato perché utilizzato dai seguenti widget:<br/>{{widgetsList}}",
"duplicate-alias-error": "Trovato un duplicato dell'alias '{{alias}}'.<br>Gli alias dell'entità devono essere univoci all'interno della dashboard.", "duplicate-alias-error": "Trovato un duplicato dell'alias '{{alias}}'.<br/>Gli alias dell'entità devono essere univoci all'interno della dashboard.",
"missing-entity-filter-error": "Filter is missing for alias '{{alias}}'.", "missing-entity-filter-error": "Manca il filtro per l'alias '{{alias}}'.",
"configure-alias": "Configura '{{alias}}' alias", "configure-alias": "Configura '{{alias}}' alias",
"alias": "Alias", "alias": "Alias",
"alias-required": "Alias entità obbligatorio.", "alias-required": "Alias entità obbligatorio.",
@ -701,6 +723,10 @@
"type-assets": "Asset", "type-assets": "Asset",
"list-of-assets": "{ count, plural, 1 {Un asset} other {Lista di # asset} }", "list-of-assets": "{ count, plural, 1 {Un asset} other {Lista di # asset} }",
"asset-name-starts-with": "Asset i cui nomi iniziano per '{{prefix}}'", "asset-name-starts-with": "Asset i cui nomi iniziano per '{{prefix}}'",
"type-entity-view": "Vista entità",
"type-entity-views": "Viste entità",
"list-of-entity-views": "{ count, plural, 1 {Una vista entità} other {Lista di # viste entità} }",
"entity-view-name-starts-with": "Viste entità i cui nomi iniziano per '{{prefix}}'",
"type-rule": "Regola", "type-rule": "Regola",
"type-rules": "Regole", "type-rules": "Regole",
"list-of-rules": "{ count, plural, 1 {Una regola} other {Lista di # regole} }", "list-of-rules": "{ count, plural, 1 {Una regola} other {Lista di # regole} }",
@ -711,7 +737,7 @@
"plugin-name-starts-with": "Plugin i cui nomi iniziano per '{{prefix}}'", "plugin-name-starts-with": "Plugin i cui nomi iniziano per '{{prefix}}'",
"type-tenant": "Tenant", "type-tenant": "Tenant",
"type-tenants": "Tenants", "type-tenants": "Tenants",
"list-of-tenants": "{ count, plural, 1 {One tenant} other {List of # tenants} }", "list-of-tenants": "{ count, plural, 1 {One tenant} other {Lista di # tenants} }",
"tenant-name-starts-with": "Tenants whose names start with '{{prefix}}'", "tenant-name-starts-with": "Tenants whose names start with '{{prefix}}'",
"type-customer": "Cliente", "type-customer": "Cliente",
"type-customers": "Clienti", "type-customers": "Clienti",
@ -719,7 +745,7 @@
"customer-name-starts-with": "Clienti i cui nomi iniziano per '{{prefix}}'", "customer-name-starts-with": "Clienti i cui nomi iniziano per '{{prefix}}'",
"type-user": "Utente", "type-user": "Utente",
"type-users": "Utenti", "type-users": "Utenti",
"list-of-users": "{ count, plural, 1 {Un utente} other {Lista of # utenti} }", "list-of-users": "{ count, plural, 1 {Un utente} other {Lista di # utenti} }",
"user-name-starts-with": "Utenti i cui nomi iniziano per '{{prefix}}'", "user-name-starts-with": "Utenti i cui nomi iniziano per '{{prefix}}'",
"type-dashboard": "Dashboard", "type-dashboard": "Dashboard",
"type-dashboards": "Dashboard", "type-dashboards": "Dashboard",
@ -730,16 +756,117 @@
"list-of-alarms": "{ count, plural, 1 {Un allarme} other {Lista di # allarmi} }", "list-of-alarms": "{ count, plural, 1 {Un allarme} other {Lista di # allarmi} }",
"alarm-name-starts-with": "Allarmi i cui nomi iniziano per '{{prefix}}'", "alarm-name-starts-with": "Allarmi i cui nomi iniziano per '{{prefix}}'",
"type-rulechain": "Rule chain", "type-rulechain": "Rule chain",
"type-rulechains": "Rule chains", "type-rulechains": "Rule chain",
"list-of-rulechains": "{ count, plural, 1 {One rule chain} other {List of # rule chains} }", "list-of-rulechains": "{ count, plural, 1 {Una rule chain} other {Lista di # catene di regole} }",
"rulechain-name-starts-with": "Rule chains whose names start with '{{prefix}}'", "rulechain-name-starts-with": "Catene di regole i cui nomi iniziano per '{{prefix}}'",
"type-rulenode": "Nodo regola",
"type-rulenodes": "Nodi regola",
"list-of-rulenodes": "{ count, plural, 1 {Un nodo regola} other {Lista di # nodi regola} }",
"rulenode-name-starts-with": "Nodi regola i cui nomi iniziano per '{{prefix}}'",
"type-current-customer": "Cliente attuale", "type-current-customer": "Cliente attuale",
"search": "Ricerca entità", "search": "Ricerca entità",
"selected-entities": "{ count, plural, 1 {1 entità selezionata} other {# entità selezionate} }", "selected-entities": "{ count, plural, 1 {1 entità selezionata} other {# entità selezionate} }",
"entity-name": "Nome entità", "entity-name": "Nome entità",
"details": "Dettagli entità", "details": "Dettagli entità",
"no-entities-prompt": "Nessuna entità trovata", "no-entities-prompt": "Nessuna entità trovata",
"no-data": "Nessun dato da mostrare" "no-data": "Nessun dato da mostrare",
"columns-to-display": "Colonne da mostrare"
},
"entity-view": {
"entity-view": "Vista entità",
"entity-view-required": "Vista entità richiesta.",
"entity-views": "Viste entità",
"management": "Gestione viste entità",
"view-entity-views": "Visualizza Viste entità",
"entity-view-alias": "Alias vista entità",
"aliases": "Alias vista entità",
"no-alias-matching": "'{{alias}}' non trovato.",
"no-aliases-found": "Nessun alias trovato.",
"no-key-matching": "'{{key}}' non trovata.",
"no-keys-found": "Nessuna chiave trovata.",
"create-new-alias": "Creane uno nuovo!",
"create-new-key": "Creane una nuova!",
"duplicate-alias-error": "Sono stati trovati dei duplicati dell'alias '{{alias}}'.<br/>Gli alias di una vista entità devono essere univoci all'interno della dashboard.",
"configure-alias": "Configura alias '{{alias}}'",
"no-entity-views-matching": "Nessuna vista entità corrispondente a '{{entity}}' è stata trovato.",
"alias": "Alias",
"alias-required": "Alias vista entità richiesto.",
"remove-alias": "Rimuovi alias vista entità",
"add-alias": "Aggiungi alias vista entità",
"name-starts-with": "Vista entità il cui nome inizia per",
"entity-view-list": "Lista viste entità",
"use-entity-view-name-filter": "Usa filtro",
"entity-view-list-empty": "Nessuna vista entità selezionata.",
"entity-view-name-filter-required": "Filtro nome vista entità obbligatorio.",
"entity-view-name-filter-no-entity-view-matched": "Nessuna vista entità il cui nome inizia per '{{entity-view}}' è stata trovata.",
"add": "Aggiungi Vista entità",
"assign-to-customer": "Assegna al cliente",
"assign-entity-view-to-customer": "Assegna vista entità/viste entità al Cliente",
"assign-entity-view-to-customer-text": "Seleziona la vista entità da assegnare al cliente",
"no-entity-views-text": "Nessuna vista entità trovata",
"assign-to-customer-text": "Seleziona il cliente a cui assegnare la vista entità/le vista entità",
"entity-view-details": "Dettagli vista entità",
"add-entity-view-text": "Aggiungi nuova vista entità",
"delete": "Elimina vista entità",
"assign-entity-views": "Assegna viste entità",
"assign-entity-views-text": "Assegna { count, plural, 1 {1 vista entità} other {# viste entità} } al cliente",
"delete-entity-views": "Elimina viste entità",
"unassign-from-customer": "Annulla assegnazione al cliente",
"unassign-entity-views": "Annulla assegnazione viste entità",
"unassign-entity-views-action-title": "Annulla assegnazione { count, plural, 1 {1 vista entità} other {# viste entità} } al cliente",
"assign-new-entity-view": "Assegna nuova vista entità",
"delete-entity-view-title": "Sei sicuro di voler eliminare la vista entità '{{entity-viewName}}'?",
"delete-entity-view-text": "Attenzione, dopo la conferma la vista entità e tutti i suoi dati non saranno più recuperabili.",
"delete-entity-views-title": "Sei sicuro di voler eliminare { count, plural, 1 {1 vista entità} other {# viste entità} }?",
"delete-entity-views-action-title": "Elimina { count, plural, 1 {1 vista entità} other {# viste entità} }",
"delete-entity-views-text": "Attenzione, dopo la conferma tutte le vista entità selezionati saranno eliminate e i relativi dati non saranno più recuperabili.",
"unassign-entity-view-title": "Sei sicuro di voler annullare l'assegnazione della vista entità '{{entity-viewName}}'?",
"unassign-entity-view-text": "Dopo la conferma sarà annullata l'assegnazione della vista entità e questa non sarà più accessibile dal cliente.",
"unassign-entity-view": "Annulla assegnazione vista entità",
"unassign-entity-views-title": "Sei sicuro di voler annullare l'assegnazione di { count, plural, 1 {1 vista entità} other {# viste entità} }?",
"unassign-entity-views-text": "Dopo la conferma sarà annullata l'assegnazione di tutte le vista entità selezionate e queste non saranno più accessibili dal cliente.",
"entity-view-type": "Tipo vista entità",
"entity-view-type-required": "Tipo vista entità obbligatorio.",
"select-entity-view-type": "Seleziona tipo vista entità",
"enter-entity-view-type": "Inserisci tipo vista entità",
"any-entity-view": "Qualsiasi vista entità",
"no-entity-view-types-matching": "Nessuna vista entità corrispondente a '{{entitySubtype}}' è stata trovata.",
"entity-view-type-list-empty": "Nessun tipo di vista entità selezionato.",
"entity-view-types": "Tipi vista entità",
"name": "Nome",
"name-required": "Nome obbligatorio.",
"description": "Descrizione",
"events": "Eventi",
"details": "Dettagli",
"copyId": "Copia Id vista entità",
"assignedToCustomer": "Assegnata al cliente",
"unable-entity-view-device-alias-title": "Impossibile rimuovere l'alias del vista entità",
"unable-entity-view-device-alias-text": "L'alias del vista entità '{{entity-viewAlias}}' non può essere eliminato perché utilizzato dai seguenti widget:<br/>{{widgetsList}}",
"select-entity-view": "Seleziona vista entità",
"make-public": "Rendi pubblica la vista entità",
"make-private": "Rendi privata la vista entità",
"start-date": "Data inizio",
"start-ts": "Ora inizio",
"end-date": "Data fine",
"end-ts": "Ora fine",
"date-limits": "Limiti temporali",
"client-attributes": "Attributi cliente",
"shared-attributes": "Attributi condivisi",
"server-attributes": "Attributi server",
"timeseries": "Serie temporali",
"client-attributes-placeholder": "Attributi cliente",
"shared-attributes-placeholder": "Attributi condivisi",
"server-attributes-placeholder": "Attributi server",
"timeseries-placeholder": "Serie temporali",
"target-entity": "Entità target",
"attributes-propagation": "Propagazione degli attributi",
"attributes-propagation-hint": "La vista entità copierà automaticamente gli attributi specificati dall'entità target ogni volta che questa vista entità sarà salvata e aggiornata. Per ragioni di performance, gli attributi dell'entità target non sono propagati alle viste entità ogni cambiamento di attributo. È possibile abilitare la propagazione automatica configurando il nodo regola \"Copia alla vista\" nella rule chain e collegando i messaggi \"Post attributes\" a \"Attributes Updated\" al nuovo nodo regola.",
"timeseries-data": "Dati delle serie temporali",
"timeseries-data-hint": "Imposta le chiavi delle serie temporali dell'entità target che saranno accessibili alla vista entità. Questi dati sono di sola lettura.",
"make-public-entity-view-title": "Sei sicuro di voler rendere pubblica la vista entità '{{entity-viewName}}'?",
"make-public-entity-view-text": "Dopo la conferma la vista entità e tutti i suoi dati saranno resi pubblici e accessibili dagli altri.",
"make-private-entity-view-title": "Sei sicuro di voler rendere privata la vista entità '{{entity-viewName}}'?",
"make-private-entity-view-text": "Dopo la conferma la vista entità e tutti i suoi dati saranno resi privati e non più accessibili da altri utenti."
}, },
"event": { "event": {
"event-type": "Tipo evento", "event-type": "Tipo evento",
@ -795,8 +922,8 @@
"token": "Token di sicurezza", "token": "Token di sicurezza",
"add-converter": "Aggiungi convertitore", "add-converter": "Aggiungi convertitore",
"add-config": "Aggiungi configurazione convertitore", "add-config": "Aggiungi configurazione convertitore",
"device-name-expression": "Device name expression", "device-name-expression": "Espressione nome dispositivo",
"device-type-expression": "Device type expression", "device-type-expression": "Espressione tipo dispositivo",
"custom": "Custom", "custom": "Custom",
"to-double": "To Double", "to-double": "To Double",
"transformer": "Transformer", "transformer": "Transformer",
@ -817,7 +944,7 @@
"credentials": "Credenziali", "credentials": "Credenziali",
"username": "Nome utente", "username": "Nome utente",
"password": "Password", "password": "Password",
"retry-interval": "Retry interval in milliseconds", "retry-interval": "Intervallo di ripetizione in millisecondi",
"anonymous": "Anonimo", "anonymous": "Anonimo",
"basic": "Basic", "basic": "Basic",
"pem": "PEM", "pem": "PEM",
@ -886,6 +1013,8 @@
"modbus-add-server": "Aggiungi server/slave", "modbus-add-server": "Aggiungi server/slave",
"modbus-add-server-prompt": "Aggiungi server/slave", "modbus-add-server-prompt": "Aggiungi server/slave",
"modbus-transport": "Transport", "modbus-transport": "Transport",
"modbus-tcp-reconnect": "Riconnessione automatica",
"modbus-rtu-over-tcp": "RTU over TCP",
"modbus-port-name": "Nome porta seriale", "modbus-port-name": "Nome porta seriale",
"modbus-encoding": "Codifica", "modbus-encoding": "Codifica",
"modbus-parity": "Parità", "modbus-parity": "Parità",
@ -1077,9 +1206,9 @@
"name-required": "Nome obbligatorio.", "name-required": "Nome obbligatorio.",
"description": "Descrizione", "description": "Descrizione",
"add": "Aggiungi Rule Chain", "add": "Aggiungi Rule Chain",
"set-root": "Make rule chain root", "set-root": "Imposta la rule chain come root",
"set-root-rulechain-title": "Are you sure you want to make the rule chain '{{ruleChainName}}' root?", "set-root-rulechain-title": "Sei sicuro di voler impostare la rule chain '{{ruleChainName}}' come root?",
"set-root-rulechain-text": "After the confirmation the rule chain will become root and will handle all incoming transport messages.", "set-root-rulechain-text": "Dopo la conferma la rule chain diverrà root a gestirà tutti i messaggi in arrivo.",
"delete-rulechain-title": "Sei sicuro di voler eliminare la rule chain '{{ruleChainName}}'?", "delete-rulechain-title": "Sei sicuro di voler eliminare la rule chain '{{ruleChainName}}'?",
"delete-rulechain-text": "Attenzione, dopo la conferma la rule chain e tutti i dati relativi non saranno più recuperabili.", "delete-rulechain-text": "Attenzione, dopo la conferma la rule chain e tutti i dati relativi non saranno più recuperabili.",
"delete-rulechains-title": "Sei sicuro di voler eliminare { count, plural, 1 {1 rule chain} other {# rule chain} }?", "delete-rulechains-title": "Sei sicuro di voler eliminare { count, plural, 1 {1 rule chain} other {# rule chain} }?",
@ -1110,33 +1239,38 @@
"events": "Eventi", "events": "Eventi",
"search": "Ricerca nodi", "search": "Ricerca nodi",
"open-node-library": "Apri libreria nodi", "open-node-library": "Apri libreria nodi",
"add": "Add rule node", "add": "Aggiungi nodo regola",
"name": "Nome", "name": "Nome",
"name-required": "Nome obbligatorio.", "name-required": "Nome obbligatorio.",
"type": "Tipo", "type": "Tipo",
"description": "Descrizione", "description": "Descrizione",
"delete": "Delete rule node", "delete": "Elimina nodo regola",
"select-all-objects": "Seleziona tutti i nodi e le connessioni", "select-all-objects": "Seleziona tutti i nodi e le connessioni",
"deselect-all-objects": "Deselect all nodes and connections", "deselect-all-objects": "Deseleziona tutti i nodi e le connessioni",
"delete-selected-objects": "Cancella nodi e connessioni selezionate", "delete-selected-objects": "Cancella nodi e connessioni selezionate",
"delete-selected": "Delete selected", "delete-selected": "Elimina selezionati",
"select-all": "Seleziona tutto", "select-all": "Seleziona tutto",
"copy-selected": "Copia selezionata", "copy-selected": "Copia selezionata",
"deselect-all": "Deseleziona tutto", "deselect-all": "Deseleziona tutto",
"rulenode-details": "Rule node details", "rulenode-details": "Dettagli nodo regola",
"debug-mode": "Modalità debug", "debug-mode": "Modalità debug",
"configuration": "Configurazione", "configuration": "Configurazione",
"link": "Link", "link": "Link",
"link-details": "Rule node link details", "link-details": "Dettagli link nodo regola",
"add-link": "Aggiungi link", "add-link": "Aggiungi link",
"link-label": "Etichetta link", "link-label": "Etichetta link",
"link-label-required": "Etichetta link obbligatoria.", "link-label-required": "Etichetta link obbligatoria.",
"custom-link-label": "Custom link label", "custom-link-label": "Etichetta link personalizzata",
"custom-link-label-required": "Custom link label is required.", "custom-link-label-required": "Etichetta link personalizzata obbligatoria.",
"link-labels": "Etichette link",
"link-labels-required": "Etichette link richieste.",
"no-link-labels-found": "Nessuna etichetta link trovata.",
"no-link-label-matching": "'{{label}}' non trovata.",
"create-new-link-label": "Creane una nuova!",
"type-filter": "Filtro", "type-filter": "Filtro",
"type-filter-details": "Filter incoming messages with configured conditions", "type-filter-details": "Filtra i messaggi in arrivo con le condizioni configurate",
"type-enrichment": "Enrichment", "type-enrichment": "Enrichment",
"type-enrichment-details": "Add additional information into Message Metadata", "type-enrichment-details": "Aggiungi informazioni addizionali nei metadati del messaggio",
"type-transformation": "Transformation", "type-transformation": "Transformation",
"type-transformation-details": "Change Message payload and Metadata", "type-transformation-details": "Change Message payload and Metadata",
"type-action": "Azioni", "type-action": "Azioni",
@ -1147,12 +1281,15 @@
"type-rule-chain-details": "Forwards incoming messages to specified Rule Chain", "type-rule-chain-details": "Forwards incoming messages to specified Rule Chain",
"type-input": "Input", "type-input": "Input",
"type-input-details": "Logical input of Rule Chain, forwards incoming messages to next related Rule Node", "type-input-details": "Logical input of Rule Chain, forwards incoming messages to next related Rule Node",
"type-unknown": "Sconosciuto",
"type-unknown-details": "Nodo regola non trovato",
"directive-is-not-loaded": "Defined configuration directive '{{directiveName}}' is not available.", "directive-is-not-loaded": "Defined configuration directive '{{directiveName}}' is not available.",
"ui-resources-load-error": "Failed to load configuration ui resources.", "ui-resources-load-error": "Failed to load configuration ui resources.",
"invalid-target-rulechain": "Unable to resolve target rule chain!", "invalid-target-rulechain": "Unable to resolve target rule chain!",
"test-script-function": "Test script function", "test-script-function": "Test script function",
"message": "Messaggio", "message": "Messaggio",
"message-type": "Tipo messaggio", "message-type": "Tipo messaggio",
"select-message-type": "Seleziona tipo messaggio",
"message-type-required": "Tipo messaggio obbligatorio", "message-type-required": "Tipo messaggio obbligatorio",
"metadata": "Metadata", "metadata": "Metadata",
"metadata-required": "Metadata entries can't be empty.", "metadata-required": "Metadata entries can't be empty.",
@ -1200,13 +1337,13 @@
}, },
"timewindow": { "timewindow": {
"days": "{ days, plural, 1 { giorno } other {# giorni } }", "days": "{ days, plural, 1 { giorno } other {# giorni } }",
"hours": "{ hours, plural, 0 { hour } 1 {1 ora } other {# ore } }", "hours": "{ hours, plural, 0 { ora } 1 {1 ora } other {# ore } }",
"minutes": "{ minutes, plural, 0 { minute } 1 {1 minuto } other {# minuti } }", "minutes": "{ minutes, plural, 0 { minuto } 1 {1 minuto } other {# minuti } }",
"seconds": "{ seconds, plural, 0 { second } 1 {1 secondo } other {# secondi } }", "seconds": "{ seconds, plural, 0 { secondo } 1 {1 secondo } other {# secondi } }",
"realtime": "Realtime", "realtime": "Realtime",
"history": "Cronologia", "history": "Cronologia",
"last-prefix": "ultimo", "last-prefix": "ultimo",
"period": "from {{ startTime }} to {{ endTime }}", "period": "da {{ startTime }} a {{ endTime }}",
"edit": "Modifica intervallo temporale", "edit": "Modifica intervallo temporale",
"date-range": "Intervallo date", "date-range": "Intervallo date",
"last": "Ultimo", "last": "Ultimo",
@ -1215,7 +1352,7 @@
"user": { "user": {
"user": "Utente", "user": "Utente",
"users": "Utenti", "users": "Utenti",
"customer-users": "Customer Users", "customer-users": "Utente cliente",
"tenant-admins": "Amministratori Tenant", "tenant-admins": "Amministratori Tenant",
"sys-admin": "Amministratore di sistema", "sys-admin": "Amministratore di sistema",
"tenant-admin": "Amministratore tenant", "tenant-admin": "Amministratore tenant",
@ -1232,7 +1369,7 @@
"delete-users-action-title": "Elimina { count, plural, 1 {1 utente} other {# utenti} }", "delete-users-action-title": "Elimina { count, plural, 1 {1 utente} other {# utenti} }",
"delete-users-text": "Attenzione, dopo la conferma tutti gli utenti selezionati saranno eliminati e tutti i relativi dati non saranno più recuperabili.", "delete-users-text": "Attenzione, dopo la conferma tutti gli utenti selezionati saranno eliminati e tutti i relativi dati non saranno più recuperabili.",
"activation-email-sent-message": "Email di attivazione inviata con successo!", "activation-email-sent-message": "Email di attivazione inviata con successo!",
"resend-activation": "Resend activation", "resend-activation": "Invia di nuovo attivazione",
"email": "Email", "email": "Email",
"email-required": "Email obbligatoria.", "email-required": "Email obbligatoria.",
"invalid-email-format": "Formato email non valido.", "invalid-email-format": "Formato email non valido.",
@ -1251,7 +1388,9 @@
"activation-link-text": "Per attivare l'utente utilizza il seguente <a href='{{activationLink}}' target='_blank'>link di attivazione</a> :", "activation-link-text": "Per attivare l'utente utilizza il seguente <a href='{{activationLink}}' target='_blank'>link di attivazione</a> :",
"copy-activation-link": "Copia link di attivazione", "copy-activation-link": "Copia link di attivazione",
"activation-link-copied-message": "Link di attivazione utente copiato negli appunti", "activation-link-copied-message": "Link di attivazione utente copiato negli appunti",
"details": "Dettagli" "details": "Dettagli",
"login-as-tenant-admin": "Accedi come Amministratore tenant",
"login-as-customer-user": "Accedi come Utente cliente"
}, },
"value": { "value": {
"type": "Tipo valore", "type": "Tipo valore",
@ -1274,7 +1413,7 @@
"select-widgets-bundle": "Seleziona bundle widget", "select-widgets-bundle": "Seleziona bundle widget",
"management": "Gestione widget", "management": "Gestione widget",
"editor": "Editor Widget", "editor": "Editor Widget",
"widget-type-not-found": "Problem loading widget configuration.<br>Probably associated\n widget type was removed.", "widget-type-not-found": "Problem loading widget configuration.<br/>Probably associated\n widget type was removed.",
"widget-type-load-error": "Widget non caricato a causa dei seguenti errori:", "widget-type-load-error": "Widget non caricato a causa dei seguenti errori:",
"remove": "Elimina widget", "remove": "Elimina widget",
"edit": "Modifica widget", "edit": "Modifica widget",
@ -1295,7 +1434,7 @@
"saveAs": "Salva widget come", "saveAs": "Salva widget come",
"save-widget-type-as": "Salva tipo widget come", "save-widget-type-as": "Salva tipo widget come",
"save-widget-type-as-text": "Please enter new widget title and/or select target widgets bundle", "save-widget-type-as-text": "Please enter new widget title and/or select target widgets bundle",
"toggle-fullscreen": "Toggle fullscreen", "toggle-fullscreen": "Commuta modalità schermo intero",
"run": "Esegui widget", "run": "Esegui widget",
"title": "Titolo widget", "title": "Titolo widget",
"title-required": "Titolo widget obbligatorio.", "title-required": "Titolo widget obbligatorio.",
@ -1308,7 +1447,7 @@
"tidy": "Tidy", "tidy": "Tidy",
"css": "CSS", "css": "CSS",
"settings-schema": "Impostazioni schema", "settings-schema": "Impostazioni schema",
"datakey-settings-schema": "Data key settings schema", "datakey-settings-schema": "Impostazioni Data key schema",
"javascript": "Javascript", "javascript": "Javascript",
"remove-widget-type-title": "Sei sicuro di voler rimuovere il tipo di widget '{{widgetName}}'?", "remove-widget-type-title": "Sei sicuro di voler rimuovere il tipo di widget '{{widgetName}}'?",
"remove-widget-type-text": "Dopo la conferma il tipo di widget e tutti i suoi dati non saranno più recuperabili.", "remove-widget-type-text": "Dopo la conferma il tipo di widget e tutti i suoi dati non saranno più recuperabili.",
@ -1367,7 +1506,7 @@
"general-settings": "Impostazioni generali", "general-settings": "Impostazioni generali",
"display-title": "Mostra titolo", "display-title": "Mostra titolo",
"drop-shadow": "Drop shadow", "drop-shadow": "Drop shadow",
"enable-fullscreen": "Abilita fullscreen", "enable-fullscreen": "Abilita schermo intero",
"background-color": "Colore sfondo", "background-color": "Colore sfondo",
"text-color": "Colore testo", "text-color": "Colore testo",
"padding": "Padding", "padding": "Padding",
@ -1412,7 +1551,7 @@
"export": "Esporta un tipo di widget", "export": "Esporta un tipo di widget",
"export-failed-error": "Impossibile esportare il tipo di widget: {{error}}", "export-failed-error": "Impossibile esportare il tipo di widget: {{error}}",
"create-new-widget-type": "Crea un nuovo tipo di widget", "create-new-widget-type": "Crea un nuovo tipo di widget",
"widget-type-file": "Widget type file", "widget-type-file": "File tipo di widget",
"invalid-widget-type-file-error": "Impossibile importare un tipo di widget: struttura dati del widget non valida." "invalid-widget-type-file-error": "Impossibile importare un tipo di widget: struttura dati del widget non valida."
}, },
"icon": { "icon": {
@ -1423,10 +1562,11 @@
}, },
"custom": { "custom": {
"widget-action": { "widget-action": {
"action-cell-button": "Action cell button", "action-cell-button": "Pulsante azione cella",
"row-click": "On row click", "row-click": "Click sulla riga",
"marker-click": "On marker click", "polygon-click": "Click sul poligono",
"tooltip-tag-action": "Tooltip tag action" "marker-click": "Click sul marker",
"tooltip-tag-action": "Azione tooltip"
} }
}, },
"language": { "language": {
@ -1435,9 +1575,9 @@
"de_DE": "Tedesco", "de_DE": "Tedesco",
"fr_FR": "Francese", "fr_FR": "Francese",
"zh_CN": "Cinese", "zh_CN": "Cinese",
"ko_KR": "Coreano",
"en_US": "Inglese", "en_US": "Inglese",
"it_IT": "Italiano", "it_IT": "Italiano",
"ko_KR": "Coreano",
"ru_RU": "Russo", "ru_RU": "Russo",
"es_ES": "Spagnolo", "es_ES": "Spagnolo",
"ja_JA": "Giapponese", "ja_JA": "Giapponese",
@ -1445,4 +1585,4 @@
"fa_IR": "Persiana" "fa_IR": "Persiana"
} }
} }
} }

1
ui/src/app/locale/locale.constant-ja_JA.json

@ -1442,6 +1442,7 @@
"widget-action": { "widget-action": {
"action-cell-button": "アクションセルボタン", "action-cell-button": "アクションセルボタン",
"row-click": "行のクリック", "row-click": "行のクリック",
"polygon-click": "ポリゴンクリック",
"marker-click": "マーカークリック", "marker-click": "マーカークリック",
"tooltip-tag-action": "ツールチップのタグアクション" "tooltip-tag-action": "ツールチップのタグアクション"
} }

1
ui/src/app/locale/locale.constant-ko_KR.json

@ -1318,6 +1318,7 @@
"widget-action": { "widget-action": {
"action-cell-button": "Action cell button", "action-cell-button": "Action cell button",
"row-click": "On row click", "row-click": "On row click",
"polygon-click": "On polygon click",
"marker-click": "On marker click", "marker-click": "On marker click",
"tooltip-tag-action": "Tooltip tag action" "tooltip-tag-action": "Tooltip tag action"
} }

1
ui/src/app/locale/locale.constant-ru_RU.json

@ -1558,6 +1558,7 @@
"action-cell-button": "Кнопка действия ячейки", "action-cell-button": "Кнопка действия ячейки",
"row-click": "Действий при щелчке на строку", "row-click": "Действий при щелчке на строку",
"marker-click": "Действия при щелчке на указателе", "marker-click": "Действия при щелчке на указателе",
"polygon-click": "Действия при щелчке на полигон",
"tooltip-tag-action": "Действие при подсказке" "tooltip-tag-action": "Действие при подсказке"
} }
}, },

3
ui/src/app/locale/locale.constant-tr_TR.json

@ -1524,7 +1524,8 @@
"widget-action": { "widget-action": {
"action-cell-button": "Eylem hücre butonu", "action-cell-button": "Eylem hücre butonu",
"row-click": "Satır tıklama eylemi", "row-click": "Satır tıklama eylemi",
"marker-click": "İşaretçi tıklama eylemi", "polygon-click": "Satır tıklama eylemi",
"marker-click": "Çokgen tıklama eylemi",
"tooltip-tag-action": "İpucu etiket eylemi" "tooltip-tag-action": "İpucu etiket eylemi"
} }
}, },

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

Loading…
Cancel
Save