From 18b2fd4664b2cb5a54b671421d577fd6e638d5a3 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Tue, 5 Apr 2022 15:01:26 +0300 Subject: [PATCH] Fix permission checks for export/import api --- .../EntitiesExportImportController.java | 94 +++++-------------- .../DefaultEntitiesExportImportService.java | 69 +++++++++----- .../sync/EntitiesExportImportService.java | 9 +- .../sync/exporting/EntityExportService.java | 5 +- .../exporting/ExportableEntitiesService.java | 14 ++- .../impl/BaseEntityExportService.java | 27 ++++-- .../sync/importing/EntityImportService.java | 5 +- .../importing/impl/AssetImportService.java | 8 +- .../impl/BaseEntityImportService.java | 92 ++++++++++++------ .../importing/impl/CustomerImportService.java | 6 +- .../impl/DashboardImportService.java | 10 +- .../importing/impl/DeviceImportService.java | 14 ++- .../impl/DeviceProfileImportService.java | 14 ++- .../impl/RuleChainImportService.java | 11 ++- .../server/dao/sql/alarm/JpaAlarmDao.java | 7 ++ .../server/dao/sql/edge/JpaEdgeDao.java | 5 + .../dao/sql/entityview/JpaEntityViewDao.java | 6 ++ .../dao/sql/resource/JpaTbResourceDao.java | 7 ++ .../server/dao/sql/rpc/JpaRpcDao.java | 7 ++ .../server/dao/sql/rule/JpaRuleNodeDao.java | 7 ++ .../server/dao/sql/tenant/JpaTenantDao.java | 6 ++ .../sql/usagerecord/JpaApiUsageStateDao.java | 7 ++ .../server/dao/sql/user/JpaUserDao.java | 7 ++ .../dao/sql/widget/JpaWidgetTypeDao.java | 7 ++ .../dao/sql/widget/JpaWidgetsBundleDao.java | 7 ++ 25 files changed, 291 insertions(+), 160 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java index f158ff15e7..f4abd1b5f7 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java @@ -39,7 +39,6 @@ import org.thingsboard.server.common.data.query.EntityKey; import org.thingsboard.server.common.data.query.EntityKeyType; import org.thingsboard.server.common.data.query.EntityTypeFilter; import org.thingsboard.server.common.data.relation.EntityRelation; -import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.sync.EntitiesExportImportService; @@ -81,9 +80,10 @@ public class EntitiesExportImportController extends BaseController { public EntityExportData> exportSingleEntity(@PathVariable EntityType entityType, @PathVariable UUID id, @RequestParam Map exportSettingsParams) throws ThingsboardException { + SecurityUser user = getCurrentUser(); EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, id); try { - return exportEntity(getTenantId(), entityId, toExportSettings(exportSettingsParams)); + return exportEntity(user, entityId, toExportSettings(exportSettingsParams)); } catch (Exception e) { throw handleException(e); } @@ -93,11 +93,12 @@ public class EntitiesExportImportController extends BaseController { public List>> exportEntitiesByIds(@PathVariable EntityType entityType, @RequestParam UUID[] ids, @RequestParam Map exportSettingsParams) throws ThingsboardException { + SecurityUser user = getCurrentUser(); List entitiesIds = Arrays.stream(ids) .map(id -> EntityIdFactory.getByTypeAndUuid(entityType, id)) .collect(Collectors.toList()); try { - return exportEntitiesByIds(getTenantId(), entitiesIds, toExportSettings(exportSettingsParams)); + return exportEntitiesByIds(user, entitiesIds, toExportSettings(exportSettingsParams)); } catch (Exception e) { throw handleException(e); } @@ -109,13 +110,13 @@ public class EntitiesExportImportController extends BaseController { @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "2147483647") int pageSize, @RequestParam(name = "customerId", required = false) UUID customerUuid) throws ThingsboardException { - TenantId tenantId = getTenantId(); + SecurityUser user = getCurrentUser(); CustomerId customerId = toCustomerId(customerUuid); EntityTypeFilter entityTypeFilter = new EntityTypeFilter(); entityTypeFilter.setEntityType(entityType); try { - return exportEntitiesByFilter(tenantId, customerId, entityTypeFilter, page, pageSize, toExportSettings(exportSettingsParams)); + return exportEntitiesByFilter(user, customerId, entityTypeFilter, page, pageSize, toExportSettings(exportSettingsParams)); } catch (Exception e) { throw handleException(e); } @@ -127,10 +128,10 @@ public class EntitiesExportImportController extends BaseController { @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "2147483647") int pageSize, @RequestParam(name = "customerId", required = false) UUID customerUuid) throws ThingsboardException { - TenantId tenantId = getTenantId(); + SecurityUser user = getCurrentUser(); CustomerId customerId = toCustomerId(customerUuid); try { - return exportEntitiesByFilter(tenantId, customerId, filter, page, pageSize, toExportSettings(exportSettingsParams)); + return exportEntitiesByFilter(user, customerId, filter, page, pageSize, toExportSettings(exportSettingsParams)); } catch (Exception e) { throw handleException(e); } @@ -141,12 +142,12 @@ public class EntitiesExportImportController extends BaseController { public List>> exportAllEntitiesByFilters(@RequestBody List filters, @RequestParam Map exportSettingsParams, @RequestParam(name = "customerId", required = false) UUID customerUuid) throws ThingsboardException { - TenantId tenantId = getTenantId(); + SecurityUser user = getCurrentUser(); CustomerId customerId = toCustomerId(customerUuid); try { List>> exportDataList = new ArrayList<>(); for (EntityFilter filter : filters) { - exportDataList.addAll(exportEntitiesByFilter(tenantId, customerId, filter, 0, Integer.MAX_VALUE, toExportSettings(exportSettingsParams))); + exportDataList.addAll(exportEntitiesByFilter(user, customerId, filter, 0, Integer.MAX_VALUE, toExportSettings(exportSettingsParams))); } return exportDataList; } catch (Exception e) { @@ -158,17 +159,17 @@ public class EntitiesExportImportController extends BaseController { public List>> exportEntitiesByQuery(@RequestBody EntityDataQuery entitiesQuery, @RequestParam Map exportSettingsParams, @RequestParam(name = "customerId", required = false) UUID customerUuid) throws ThingsboardException { - TenantId tenantId = getTenantId(); + SecurityUser user = getCurrentUser(); CustomerId customerId = toCustomerId(customerUuid); try { - return exportEntitiesByQuery(tenantId, customerId, entitiesQuery, toExportSettings(exportSettingsParams)); + return exportEntitiesByQuery(user, customerId, entitiesQuery, toExportSettings(exportSettingsParams)); } catch (Exception e) { throw handleException(e); } } - private List>> exportEntitiesByFilter(TenantId tenantId, CustomerId customerId, EntityFilter filter, int page, int pageSize, EntityExportSettings exportSettings) throws ThingsboardException { + private List>> exportEntitiesByFilter(SecurityUser user, CustomerId customerId, EntityFilter filter, int page, int pageSize, EntityExportSettings exportSettings) throws ThingsboardException { EntityDataPageLink pageLink = new EntityDataPageLink(); pageLink.setPage(page); pageLink.setPageSize(pageSize); @@ -176,43 +177,26 @@ public class EntitiesExportImportController extends BaseController { pageLink.setSortOrder(new EntityDataSortOrder(sortProperty, EntityDataSortOrder.Direction.DESC)); EntityDataQuery query = new EntityDataQuery(filter, pageLink, List.of(sortProperty), Collections.emptyList(), Collections.emptyList()); - return exportEntitiesByQuery(tenantId, customerId, query, exportSettings); + return exportEntitiesByQuery(user, customerId, query, exportSettings); } - private List>> exportEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query, EntityExportSettings exportSettings) throws ThingsboardException { - List entitiesIds = entityService.findEntityDataByQuery(tenantId, customerId, query).getData().stream() + private List>> exportEntitiesByQuery(SecurityUser user, CustomerId customerId, EntityDataQuery query, EntityExportSettings exportSettings) throws ThingsboardException { + List entitiesIds = entityService.findEntityDataByQuery(user.getTenantId(), customerId, query).getData().stream() .map(EntityData::getEntityId) .collect(Collectors.toList()); - return exportEntitiesByIds(tenantId, entitiesIds, exportSettings); + return exportEntitiesByIds(user, entitiesIds, exportSettings); } - private List>> exportEntitiesByIds(TenantId tenantId, List entitiesIds, EntityExportSettings exportSettings) throws ThingsboardException { + private List>> exportEntitiesByIds(SecurityUser user, List entitiesIds, EntityExportSettings exportSettings) throws ThingsboardException { List>> exportDataList = new ArrayList<>(); for (EntityId entityId : entitiesIds) { - exportDataList.add(exportEntity(tenantId, entityId, exportSettings)); + exportDataList.add(exportEntity(user, entityId, exportSettings)); } return exportDataList; } - private , I extends EntityId> EntityExportData exportEntity(TenantId tenantId, I entityId, EntityExportSettings exportSettings) throws ThingsboardException { - checkEntityId(entityId, Operation.READ); - - List relations = new LinkedList<>(); - if (exportSettings.isExportInboundRelations()) { - relations.addAll(relationService.findByTo(tenantId, entityId, RelationTypeGroup.COMMON)); - } - if (exportSettings.isExportOutboundRelations()) { - relations.addAll(relationService.findByFrom(tenantId, entityId, RelationTypeGroup.COMMON)); - } - for (EntityRelation relation : relations) { - if (!relation.getFrom().equals(entityId)) { - checkEntityId(relation.getFrom(), Operation.READ); - } else if (!relation.getTo().equals(entityId)) { - checkEntityId(relation.getTo(), Operation.READ); - } - } - - return exportImportService.exportEntity(tenantId, entityId, exportSettings); + private , I extends EntityId> EntityExportData exportEntity(SecurityUser user, I entityId, EntityExportSettings exportSettings) throws ThingsboardException { + return exportImportService.exportEntity(user, entityId, exportSettings); } @@ -235,41 +219,7 @@ public class EntitiesExportImportController extends BaseController { public List>> importEntities(SecurityUser user, List>> exportDataList, EntityImportSettings importSettings) throws ThingsboardException { - for (EntityExportData> exportData : exportDataList) { - ExportableEntity existingEntity = exportableEntitiesService.findEntityByExternalId(user.getTenantId(), exportData.getEntity().getId()); - if (existingEntity != null) { - accessControlService.checkPermission(user, Resource.of(exportData.getEntityType()), Operation.WRITE, existingEntity.getId(), existingEntity); - } else { - exportData.getEntity().setTenantId(user.getTenantId()); - accessControlService.checkPermission(user, Resource.of(exportData.getEntityType()), Operation.CREATE, null, exportData.getEntity()); - } - - List relations = new LinkedList<>(); - if (importSettings.isImportInboundRelations() && exportData.getInboundRelations() != null) { - relations.addAll(exportData.getInboundRelations()); - } - if (importSettings.isImportOutboundRelations() && exportData.getOutboundRelations() != null) { - relations.addAll(exportData.getOutboundRelations()); - } - for (EntityRelation relation : relations) { - EntityId otherEntityId = null; - if (!relation.getFrom().equals(exportData.getEntity().getId())) { - otherEntityId = relation.getFrom(); - } else if (!relation.getTo().equals(exportData.getEntity().getId())) { - otherEntityId = relation.getTo(); - } - if (otherEntityId != null) { - ExportableEntity otherEntity = exportableEntitiesService.findEntityByExternalId(user.getTenantId(), otherEntityId); - if (otherEntity != null) { - checkEntityId(otherEntity.getId(), Operation.WRITE); - } else { - throw new IllegalArgumentException("Relation is referencing non-existing entity"); - } - } - } - } - - return exportImportService.importEntities(user.getTenantId(), exportDataList, importSettings); + return exportImportService.importEntities(user, exportDataList, importSettings); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java index ef9da1f89d..7d98ae9004 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java @@ -15,19 +15,23 @@ */ package org.thingsboard.server.service.sync; +import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; 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.HasId; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ExportableEntityDao; -import org.thingsboard.server.dao.TenantEntityDao; import org.thingsboard.server.queue.util.TbCoreComponent; +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.sync.exporting.EntityExportService; import org.thingsboard.server.service.sync.exporting.EntityExportSettings; import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; @@ -36,7 +40,10 @@ import org.thingsboard.server.service.sync.importing.EntityImportResult; import org.thingsboard.server.service.sync.importing.EntityImportService; import org.thingsboard.server.service.sync.importing.EntityImportSettings; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -45,12 +52,15 @@ import java.util.stream.Collectors; @Service @TbCoreComponent +@RequiredArgsConstructor public class DefaultEntitiesExportImportService implements EntitiesExportImportService, ExportableEntitiesService { private final Map> exportServices = new HashMap<>(); private final Map> importServices = new HashMap<>(); private final Map> daos = new HashMap<>(); + private final AccessControlService accessControlService; + protected static final List SUPPORTED_ENTITY_TYPES = List.of( EntityType.CUSTOMER, EntityType.ASSET, EntityType.RULE_CHAIN, EntityType.DEVICE_PROFILE, EntityType.DEVICE, EntityType.DASHBOARD @@ -58,52 +68,67 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS @Override - public , I extends EntityId> EntityExportData exportEntity(TenantId tenantId, I entityId, EntityExportSettings exportSettings) { + public , I extends EntityId> EntityExportData exportEntity(SecurityUser user, I entityId, EntityExportSettings exportSettings) throws ThingsboardException { EntityType entityType = entityId.getEntityType(); EntityExportService> exportService = getExportService(entityType); - return exportService.getExportData(tenantId, entityId, exportSettings); + return exportService.getExportData(user, entityId, exportSettings); } // TODO [viacheslav]: validate export data - @Transactional + @Transactional(rollbackFor = Exception.class) @Override - public , I extends EntityId> EntityImportResult importEntity(TenantId tenantId, EntityExportData exportData, EntityImportSettings importSettings) { + public , I extends EntityId> EntityImportResult importEntity(SecurityUser user, EntityExportData exportData, EntityImportSettings importSettings) throws ThingsboardException { EntityType entityType = exportData.getEntityType(); EntityImportService> importService = getImportService(entityType); - return importService.importEntity(tenantId, exportData, importSettings); + return importService.importEntity(user, exportData, importSettings); } - @Transactional + @Transactional(rollbackFor = Exception.class) @Override - public , I extends EntityId> List> importEntities(TenantId tenantId, List> exportDataList, EntityImportSettings importSettings) { - return exportDataList.stream() - .sorted(Comparator.comparing(exportData -> SUPPORTED_ENTITY_TYPES.indexOf(exportData.getEntityType()))) - // TODO [viacheslav]: order for rule chains (depending on references) - .map(exportData -> importEntity(tenantId, exportData, importSettings)) - .collect(Collectors.toList()); + public List>> importEntities(SecurityUser user, List>> exportDataList, EntityImportSettings importSettings) throws ThingsboardException { + exportDataList.sort(Comparator.comparing(exportData -> SUPPORTED_ENTITY_TYPES.indexOf(exportData.getEntityType()))); + + List>> importResults = new ArrayList<>(); + for (EntityExportData> exportData : exportDataList) { + importResults.add(importEntity(user, exportData, importSettings)); + } + return importResults; } @Override - public , I extends EntityId> E findEntityByExternalId(TenantId tenantId, I externalId) { + public , I extends EntityId> E findEntityByExternalId(SecurityUser user, I externalId) { EntityType entityType = externalId.getEntityType(); if (SUPPORTED_ENTITY_TYPES.contains(entityType)) { ExportableEntityDao dao = (ExportableEntityDao) getDao(entityType); - E entity = dao.findByTenantIdAndExternalId(tenantId.getId(), externalId.getId()); - if (entity != null) { - return entity; - } + return dao.findByTenantIdAndExternalId(user.getTenantId().getId(), externalId.getId()); } - return findEntityById(tenantId, externalId); + return findEntityById(user, externalId); } @Override - public , I extends EntityId> E findEntityById(TenantId tenantId, I id) { + public , I extends EntityId> E findEntityById(SecurityUser user, I id) { Dao dao = (Dao) getDao(id.getEntityType()); - return dao.findById(tenantId, id.getId()); + return dao.findById(user.getTenantId(), id.getId()); + } + + + @Override + public void checkPermission(SecurityUser user, HasId entity, Operation operation) throws ThingsboardException { + if (entity instanceof HasTenantId) { + accessControlService.checkPermission(user, Resource.of(entity.getId().getEntityType()), operation, entity.getId(), (HasTenantId) entity); + } else if (entity != null) { + accessControlService.checkPermission(user, Resource.of(entity.getId().getEntityType()), operation); + } + } + + @Override + public void checkPermission(SecurityUser user, EntityId entityId, Operation operation) throws ThingsboardException { + HasId entity = findEntityById(user, entityId); + checkPermission(user, entity, operation); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java index 0fbe75a866..447d514c68 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java @@ -16,8 +16,9 @@ package org.thingsboard.server.service.sync; import org.thingsboard.server.common.data.ExportableEntity; +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; import org.thingsboard.server.service.sync.exporting.EntityExportSettings; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; import org.thingsboard.server.service.sync.importing.EntityImportResult; @@ -27,10 +28,10 @@ import java.util.List; public interface EntitiesExportImportService { - , I extends EntityId> EntityExportData exportEntity(TenantId tenantId, I entityId, EntityExportSettings exportSettings); + , I extends EntityId> EntityExportData exportEntity(SecurityUser user, I entityId, EntityExportSettings exportSettings) throws ThingsboardException; - , I extends EntityId> EntityImportResult importEntity(TenantId tenantId, EntityExportData exportData, EntityImportSettings importSettings); + , I extends EntityId> EntityImportResult importEntity(SecurityUser user, EntityExportData exportData, EntityImportSettings importSettings) throws ThingsboardException; - , I extends EntityId> List> importEntities(TenantId tenantId, List> exportDataList, EntityImportSettings importSettings); + List>> importEntities(SecurityUser user, List>> exportDataList, EntityImportSettings importSettings) throws ThingsboardException; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportService.java index 6b57cbb557..681a398720 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportService.java @@ -17,13 +17,14 @@ package org.thingsboard.server.service.sync.exporting; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; +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; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; public interface EntityExportService, D extends EntityExportData> { - D getExportData(TenantId tenantId, I entityId, EntityExportSettings exportSettings); + D getExportData(SecurityUser user, I entityId, EntityExportSettings exportSettings) throws ThingsboardException; EntityType getEntityType(); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java index 9a47e98920..c46ea14b10 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java @@ -16,14 +16,22 @@ package org.thingsboard.server.service.sync.exporting; import org.thingsboard.server.common.data.ExportableEntity; +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.HasId; -import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.permission.Operation; public interface ExportableEntitiesService { - , I extends EntityId> E findEntityByExternalId(TenantId tenantId, I externalId); + , I extends EntityId> E findEntityByExternalId(SecurityUser user, I externalId); - , I extends EntityId> E findEntityById(TenantId tenantId, I id); + , I extends EntityId> E findEntityById(SecurityUser user, I id); + + + void checkPermission(SecurityUser user, HasId entity, Operation operation) throws ThingsboardException; + + void checkPermission(SecurityUser user, EntityId entityId, Operation operation) throws ThingsboardException; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java index a3a83196bb..66558634a4 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java @@ -18,14 +18,17 @@ package org.thingsboard.server.service.sync.exporting.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.thingsboard.server.common.data.ExportableEntity; +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.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.relation.RelationService; -import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.sync.exporting.EntityExportService; import org.thingsboard.server.service.sync.exporting.EntityExportSettings; +import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; import java.util.List; @@ -38,19 +41,31 @@ public abstract class BaseEntityExportService inboundRelations = relationService.findByTo(tenantId, entityId, RelationTypeGroup.COMMON); + List inboundRelations = relationService.findByTo(user.getTenantId(), entityId, RelationTypeGroup.COMMON); + if (inboundRelations != null) { + for (EntityRelation relation : inboundRelations) { + exportableEntitiesService.checkPermission(user, relation.getFrom(), Operation.READ); + } + } exportData.setInboundRelations(inboundRelations); } if (exportSettings.isExportOutboundRelations()) { - List outboundRelations = relationService.findByFrom(tenantId, entityId, RelationTypeGroup.COMMON); + List outboundRelations = relationService.findByFrom(user.getTenantId(), entityId, RelationTypeGroup.COMMON); + if (outboundRelations != null) { + for (EntityRelation relation : outboundRelations) { + exportableEntitiesService.checkPermission(user, relation.getTo(), Operation.READ); + } + } exportData.setOutboundRelations(outboundRelations); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportService.java index 19947e8917..e3e40eea2a 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportService.java @@ -17,13 +17,14 @@ package org.thingsboard.server.service.sync.importing; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; +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; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; public interface EntityImportService, D extends EntityExportData> { - EntityImportResult importEntity(TenantId tenantId, D exportData, EntityImportSettings importSettings); + EntityImportResult importEntity(SecurityUser user, D exportData, EntityImportSettings importSettings) throws ThingsboardException; EntityType getEntityType(); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java index b9013fd549..326e15a91b 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java @@ -33,9 +33,13 @@ public class AssetImportService extends BaseEntityImportService importEntity(TenantId tenantId, D exportData, EntityImportSettings importSettings) { + public EntityImportResult importEntity(SecurityUser user, D exportData, EntityImportSettings importSettings) throws ThingsboardException { E entity = exportData.getEntity(); - E existingEntity = exportableEntitiesService.findEntityByExternalId(tenantId, entity.getId()); + E existingEntity = exportableEntitiesService.findEntityByExternalId(user, entity.getId()); entity.setExternalId(entity.getId()); + NewIdProvider idProvider = new NewIdProvider(user, entity, existingEntity, importSettings); + setOwner(user.getTenantId(), entity, idProvider); if (existingEntity == null) { entity.setId(null); + exportableEntitiesService.checkPermission(user, entity, Operation.CREATE); } else { entity.setId(existingEntity.getId()); + exportableEntitiesService.checkPermission(user, existingEntity, Operation.WRITE); } - E savedEntity = prepareAndSave(tenantId, entity, exportData, new NewIdProvider(entity, existingEntity, importSettings)); - importRelations(tenantId, savedEntity, existingEntity, exportData, importSettings); + E savedEntity = prepareAndSave(user.getTenantId(), entity, exportData, idProvider); + importRelations(user, savedEntity, existingEntity, exportData, importSettings); EntityImportResult importResult = new EntityImportResult<>(); importResult.setSavedEntity(savedEntity); @@ -73,57 +80,71 @@ public abstract class BaseEntityImportService newRelations = new LinkedList<>(); if (importSettings.isImportInboundRelations() && CollectionUtils.isNotEmpty(exportData.getInboundRelations())) { newRelations.addAll(exportData.getInboundRelations().stream() - .peek(relation -> { - relation.setTo(savedEntity.getId()); - relation.setFrom(getInternalId(tenantId, relation.getFrom())); - }) + .peek(relation -> relation.setTo(savedEntity.getId())) .collect(Collectors.toList())); + if (importSettings.isRemoveExistingRelations() && existingEntity != null) { - relationService.findByTo(tenantId, savedEntity.getId(), RelationTypeGroup.COMMON).forEach(existingRelation -> { - relationService.deleteRelation(tenantId, existingRelation); - }); + for (EntityRelation existingRelation : relationService.findByTo(user.getTenantId(), savedEntity.getId(), RelationTypeGroup.COMMON)) { + exportableEntitiesService.checkPermission(user, existingRelation.getFrom(), Operation.WRITE); + relationService.deleteRelation(user.getTenantId(), existingRelation); + } } } if (importSettings.isImportOutboundRelations() && CollectionUtils.isNotEmpty(exportData.getOutboundRelations())) { newRelations.addAll(exportData.getOutboundRelations().stream() - .peek(relation -> { - relation.setTo(getInternalId(tenantId, relation.getTo())); - relation.setFrom(savedEntity.getId()); - }) + .peek(relation -> relation.setFrom(savedEntity.getId())) .collect(Collectors.toList())); + if (importSettings.isRemoveExistingRelations() && existingEntity != null) { - relationService.findByFrom(tenantId, savedEntity.getId(), RelationTypeGroup.COMMON).forEach(existingRelation -> { - relationService.deleteRelation(tenantId, existingRelation); - }); + for (EntityRelation existingRelation : relationService.findByFrom(user.getTenantId(), savedEntity.getId(), RelationTypeGroup.COMMON)) { + exportableEntitiesService.checkPermission(user, existingRelation.getTo(), Operation.WRITE); + relationService.deleteRelation(user.getTenantId(), existingRelation); + } } } - newRelations.forEach(relation -> { - relationService.saveRelation(tenantId, relation); - }); + for (EntityRelation relation : newRelations) { + HasId otherEntity = null; + if (!relation.getTo().equals(savedEntity.getId())) { + otherEntity = findInternalEntity(user, relation.getTo()); + relation.setTo(otherEntity.getId()); + } + if (!relation.getFrom().equals(savedEntity.getId())) { + otherEntity = findInternalEntity(user, relation.getFrom()); + relation.setFrom(otherEntity.getId()); + } + if (otherEntity != null) { + exportableEntitiesService.checkPermission(user, otherEntity, Operation.WRITE); + } + + relationService.saveRelation(user.getTenantId(), relation); + } } - private ID getInternalId(TenantId tenantId, ID externalId) { + private , ID extends EntityId> IE findInternalEntity(SecurityUser user, ID externalId) { if (externalId == null || externalId.isNullUid()) { return null; } - HasId entity = exportableEntitiesService.findEntityByExternalId(tenantId, externalId); + IE entity = exportableEntitiesService.findEntityByExternalId(user, externalId); if (entity == null) { throw new IllegalArgumentException("Cannot find " + externalId.getEntityType() + " by external id " + externalId); } - return entity.getId(); + return entity; } @RequiredArgsConstructor protected class NewIdProvider { + private final SecurityUser user; private final E entity; private final E existingEntity; private final EntityImportSettings importSettings; @@ -132,26 +153,37 @@ public abstract class BaseEntityImportService ID get(TenantId tenantId, Function idExtractor) { + public ID get(Function idExtractor) { if (existingEntity == null || importSettings.isUpdateReferencesToOtherEntities() || ALWAYS_UPDATE_REFERENCED_IDS.contains(getEntityType())) { - return getInternalId(tenantId, idExtractor.apply(entity)); + return getInternalId(idExtractor.apply(this.entity)); } else { return idExtractor.apply(existingEntity); } } - public Set get(TenantId tenantId, Function> listExtractor, Function idGetter, BiConsumer idSetter) { + public Set get(Function> listExtractor, Function idGetter, BiConsumer idSetter) { if (existingEntity == null || importSettings.isUpdateReferencesToOtherEntities()) { return Optional.ofNullable(listExtractor.apply(entity)).orElse(Collections.emptySet()).stream() .peek(t -> { - idSetter.accept(t, getInternalId(tenantId, idGetter.apply(t))); + idSetter.accept(t, getInternalId(idGetter.apply(t))); }) .collect(Collectors.toSet()); } else { return listExtractor.apply(existingEntity); } } + + private ID getInternalId(ID externalId) { + try { + HasId entity = findInternalEntity(user, externalId); + exportableEntitiesService.checkPermission(user, entity, Operation.READ); + return entity.getId(); + } catch (ThingsboardException e) { + throw new IllegalArgumentException(e.getMessage(), e); + } + } + } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java index 92f7371d50..d17e6c8b41 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java @@ -33,8 +33,12 @@ public class CustomerImportService extends BaseEntityImportService assignedCustomers = idProvider.get(tenantId, Dashboard::getAssignedCustomers, ShortCustomerInfo::getCustomerId, ShortCustomerInfo::setCustomerId); + Set assignedCustomers = idProvider.get(Dashboard::getAssignedCustomers, ShortCustomerInfo::getCustomerId, ShortCustomerInfo::setCustomerId); dashboard.setAssignedCustomers(null); dashboard = dashboardService.saveDashboard(dashboard); for (ShortCustomerInfo customerInfo : assignedCustomers) { @@ -54,7 +58,7 @@ public class DashboardImportService extends BaseEntityImportService existingAssignedCustomers = Optional.ofNullable(dashboardService.findDashboardById(tenantId, dashboard.getId()).getAssignedCustomers()) .orElse(Collections.emptySet()).stream().map(ShortCustomerInfo::getCustomerId).collect(Collectors.toSet()); - Set newAssignedCustomers = idProvider.get(tenantId, Dashboard::getAssignedCustomers, ShortCustomerInfo::getCustomerId, ShortCustomerInfo::setCustomerId).stream() + Set newAssignedCustomers = idProvider.get(Dashboard::getAssignedCustomers, ShortCustomerInfo::getCustomerId, ShortCustomerInfo::setCustomerId).stream() .map(ShortCustomerInfo::getCustomerId).collect(Collectors.toSet()); Set toUnassign = new HashSet<>(existingAssignedCustomers); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java index f35a20e312..3b844f8657 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java @@ -33,12 +33,16 @@ public class DeviceImportService extends BaseEntityImportService { @@ -54,18 +58,19 @@ public class RuleChainImportService extends BaseEntityImportService { ((ObjectNode) ruleNodeConfig).set("ruleChainId", new TextNode( - idProvider.get(tenantId, rc -> new RuleChainId(otherRuleChainUuid)).toString() + idProvider.get(rc -> new RuleChainId(otherRuleChainUuid)).toString() )); ruleNode.setConfiguration(ruleNodeConfig); }); }); Optional.ofNullable(metaData.getRuleChainConnections()).orElse(Collections.emptyList()) .forEach(ruleChainConnectionInfo -> { - ruleChainConnectionInfo.setTargetRuleChainId(idProvider.get(tenantId, rc -> ruleChainConnectionInfo.getTargetRuleChainId())); + ruleChainConnectionInfo.setTargetRuleChainId(idProvider.get(rc -> ruleChainConnectionInfo.getTargetRuleChainId())); }); ruleChain.setFirstRuleNodeId(null); if (ruleChain.getId() != null) { + // FIXME [viacheslav]: maybe no need to delete ruleChainService.deleteRuleNodes(tenantId, ruleChain.getId()); } ruleChain = ruleChainService.saveRuleChain(ruleChain); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java index cdf04f47c5..8e165898a2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java @@ -21,6 +21,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmQuery; @@ -188,4 +189,10 @@ public class JpaAlarmDao extends JpaAbstractDao implements A log.trace("[{}] Try to delete entity alarm records using [{}]", tenantId, entityId); entityAlarmRepository.deleteByEntityId(entityId.getId()); } + + @Override + public EntityType getEntityType() { + return EntityType.ALARM; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java index 4be9c08849..802c9b99b8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java @@ -193,4 +193,9 @@ public class JpaEdgeDao extends JpaAbstractSearchTextDao imple return list; } + @Override + public EntityType getEntityType() { + return EntityType.EDGE; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java index 8c515f13c0..52371706e3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java @@ -200,4 +200,10 @@ public class JpaEntityViewDao extends JpaAbstractSearchTextDao implements RpcDao public Long deleteOutdatedRpcByTenantId(TenantId tenantId, Long expirationTime) { return rpcRepository.deleteOutdatedRpcByTenantId(tenantId.getId(), expirationTime); } + + @Override + public EntityType getEntityType() { + return EntityType.RPC; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java index a499e68da8..d49b54434f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.dao.DaoUtil; @@ -50,4 +51,10 @@ public class JpaRuleNodeDao extends JpaAbstractSearchTextDao findRuleNodesByTenantIdAndType(TenantId tenantId, String type, String search) { return DaoUtil.convertDataList(ruleNodeRepository.findRuleNodesByTenantIdAndType(tenantId.getId(), type, search)); } + + @Override + public EntityType getEntityType() { + return EntityType.RULE_NODE; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java index b985d45e8c..56a75f4d3b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.sql.tenant; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantInfo; import org.thingsboard.server.common.data.id.TenantId; @@ -78,4 +79,9 @@ public class JpaTenantDao extends JpaAbstractSearchTextDao return DaoUtil.pageToPageData(tenantRepository.findTenantsIds(DaoUtil.toPageable(pageLink))).mapData(TenantId::fromUUID); } + @Override + public EntityType getEntityType() { + return EntityType.TENANT; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/usagerecord/JpaApiUsageStateDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/usagerecord/JpaApiUsageStateDao.java index 7c6801042d..96a0ecaede 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/usagerecord/JpaApiUsageStateDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/usagerecord/JpaApiUsageStateDao.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.sql.usagerecord; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.ApiUsageState; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.DaoUtil; @@ -68,4 +69,10 @@ public class JpaApiUsageStateDao extends JpaAbstractDao imple public Long countByTenantId(TenantId tenantId) { return userRepository.countByTenantId(tenantId.getId()); } + + @Override + public EntityType getEntityType() { + return EntityType.USER; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java index bbaf2529a7..eac0a15b1e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.sql.widget; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; @@ -73,4 +74,10 @@ public class JpaWidgetTypeDao extends JpaAbstractDao