diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java b/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java index 70fa3a5ed0..fb080e854d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java @@ -82,7 +82,7 @@ public class EntityQueryController extends BaseController { } @ApiOperation(value = "Find Entity Data by Query", notes = ENTITY_DATA_QUERY_DESCRIPTION) - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/entitiesQuery/find", method = RequestMethod.POST) @ResponseBody public PageData findEntityDataByQuery( diff --git a/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java index 93d67ccd4b..8e6d90dfdc 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java @@ -17,6 +17,7 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -26,6 +27,7 @@ import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.servlet.ResultActions; import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.DataConstants; @@ -33,6 +35,7 @@ import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmSeverity; @@ -62,6 +65,7 @@ import org.thingsboard.server.common.data.query.FilterPredicateValue; import org.thingsboard.server.common.data.query.KeyFilter; import org.thingsboard.server.common.data.query.NumericFilterPredicate; import org.thingsboard.server.common.data.query.RelationsQueryFilter; +import org.thingsboard.server.common.data.query.SingleEntityFilter; import org.thingsboard.server.common.data.query.StringFilterPredicate; import org.thingsboard.server.common.data.query.TsValue; import org.thingsboard.server.common.data.queue.QueueStats; @@ -69,6 +73,8 @@ import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.service.DaoSqlTest; @@ -210,6 +216,64 @@ public class EntityQueryControllerTest extends AbstractControllerTest { countByQueryAndCheck(countQuery, 97); } + @Test + public void testEDQForSysAdmin() throws Exception { + loginSysAdmin(); + + ObjectNode tenantAttr = JacksonUtil.newObjectNode(); + tenantAttr.put("attr", "tenantAttrValue"); + doPost("/api/plugins/telemetry/TENANT/" + tenantId + "/attributes/" + AttributeScope.SERVER_SCOPE, tenantAttr); + + loginTenantAdmin(); + Device tenantDevice = new Device(); + tenantDevice.setName("device " + StringUtils.randomAlphanumeric(10)); + tenantDevice.setType("default"); + tenantDevice = doPost("/api/device", tenantDevice, Device.class); + + loginSysAdmin(); + TenantProfile tenantProfile = doPost("/api/tenantProfile", createTenantProfile("Test tenant profile"), TenantProfile.class); + + ObjectNode tenantProfileAttr = JacksonUtil.newObjectNode(); + tenantProfileAttr.put("attr", "tenantProfileAttrValue"); + doPost("/api/plugins/telemetry/TENANT_PROFILE/" + tenantProfile.getId() + "/attributes/" + AttributeScope.SERVER_SCOPE, tenantProfileAttr); + + // check tenant telemetry is accessible for sysadmin + SingleEntityFilter filter = new SingleEntityFilter(); + filter.setSingleEntity(AliasEntityId.fromEntityId(tenantId)); + EntityDataSortOrder sortOrder = new EntityDataSortOrder(new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"), EntityDataSortOrder.Direction.ASC); + EntityDataPageLink pageLink = new EntityDataPageLink(10, 0, null, sortOrder); + List entityFields = List.of(new EntityKey(EntityKeyType.ENTITY_FIELD, "name")); + List latestValues = List.of(new EntityKey(EntityKeyType.ATTRIBUTE, "attr")); + EntityDataQuery dataQuery = new EntityDataQuery(filter, pageLink, entityFields, latestValues, null); + + PageData loadedTenants = findByQueryAndCheck(dataQuery, 1); + String retrievedTenantAttr = loadedTenants.getData().get(0).getLatest().get(EntityKeyType.ATTRIBUTE).get("attr").getValue(); + assertThat(retrievedTenantAttr).isEqualTo("tenantAttrValue"); + + // check tenant profile telemetry is accessible for sysadmin + filter.setSingleEntity(AliasEntityId.fromEntityId(tenantProfile.getId())); + + PageData loadedTenantProfiles = findByQueryAndCheck(dataQuery, 1); + String retrievedProfileAttr = loadedTenantProfiles.getData().get(0).getLatest().get(EntityKeyType.ATTRIBUTE).get("attr").getValue(); + assertThat(retrievedProfileAttr).isEqualTo("tenantProfileAttrValue"); + + // check other tenant entities are prohibited + filter.setSingleEntity(AliasEntityId.fromEntityId(tenantDevice.getId())); + findByQueryAndCheck(dataQuery, 0); + } + + private TenantProfile createTenantProfile(String name) { + TenantProfile tenantProfile = new TenantProfile(); + tenantProfile.setName(name); + tenantProfile.setDescription(name + " Test"); + TenantProfileData tenantProfileData = new TenantProfileData(); + tenantProfileData.setConfiguration(new DefaultTenantProfileConfiguration()); + tenantProfile.setProfileData(tenantProfileData); + tenantProfile.setDefault(false); + tenantProfile.setIsolatedTbRuleEngine(false); + return tenantProfile; + } + @Test public void testTenantCountAlarmsByQuery() throws Exception { loginTenantAdmin(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java index b512f353f5..d9deb5d891 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java @@ -135,7 +135,7 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe long startNs = System.nanoTime(); PageData result; - if (edqsService.isApiEnabled() && validForEdqs(query)) { + if (edqsService.isApiEnabled() && validForEdqs(query) && !tenantId.isSysTenantId()) { EdqsRequest request = EdqsRequest.builder() .entityDataQuery(query) .build(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java index 8f3fe71f35..45515e16bd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java @@ -540,7 +540,8 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { } private String buildPermissionQuery(SqlQueryContext ctx, EntityFilter entityFilter) { - if (ctx.isIgnorePermissionCheck()) { + if (ctx.isIgnorePermissionCheck() || (ctx.getTenantId().isSysTenantId() && + (ctx.getEntityType() == EntityType.TENANT || ctx.getEntityType() == EntityType.TENANT_PROFILE))) { return "1=1"; } switch (entityFilter.getType()) {