diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java index 9192bc4440..f692d8dd60 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -87,7 +87,7 @@ public class AlarmController extends BaseController { try { alarm.setTenantId(getCurrentUser().getTenantId()); - checkEntity(alarm.getId(), alarm, Resource.ALARM); + checkEntity(alarm.getId(), alarm, Resource.ALARM); Alarm savedAlarm = checkNotNull(alarmService.createOrUpdateAlarm(alarm)); logEntityAction(savedAlarm.getId(), savedAlarm, diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java index cd391b01b9..c88af00694 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Repository; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmStatus; @@ -63,19 +64,19 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { } public static final String SELECT_ORIGINATOR_NAME = " CASE" + - " WHEN a.originator_type = 0" + + " WHEN a.originator_type = "+ EntityType.TENANT.ordinal() + " THEN (select title from tenant where id = a.originator_id)" + - " WHEN a.originator_type = 1 " + + " WHEN a.originator_type = "+ EntityType.CUSTOMER.ordinal() + " THEN (select title from customer where id = a.originator_id)" + - " WHEN a.originator_type = 2" + + " WHEN a.originator_type = " + EntityType.USER.ordinal() + " THEN (select CONCAT (first_name, ' ', last_name) from tb_user where id = a.originator_id)" + - " WHEN a.originator_type = 3" + + " WHEN a.originator_type = " + EntityType.DASHBOARD.ordinal() + " THEN (select title from dashboard where id = a.originator_id)" + - " WHEN a.originator_type = 4" + + " WHEN a.originator_type = " + EntityType.ASSET.ordinal() + " THEN (select name from asset where id = a.originator_id)" + - " WHEN a.originator_type = 5" + + " WHEN a.originator_type = " + EntityType.DEVICE.ordinal() + " THEN (select name from device where id = a.originator_id)" + - " WHEN a.originator_type = 9" + + " WHEN a.originator_type = " + EntityType.ENTITY_VIEW.ordinal() + " THEN (select name from entity_view where id = a.originator_id)" + " END as originator_name"; @@ -114,8 +115,11 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { if (pageLink.isSearchPropagatedAlarms()) { selectPart.append(" r.from_id as entity_id "); fromPart.append(JOIN_RELATIONS); + wherePart.append(buildPermissionsQuery(tenantId, customerId, ctx)); + addAnd = true; } else { selectPart.append(" a.originator_id as entity_id "); + //No need to check permissions if we select by originator. } EntityDataSortOrder sortOrder = pageLink.getSortOrder(); if (sortOrder != null && sortOrder.getKey().getType().equals(EntityKeyType.ALARM_FIELD)) { @@ -126,8 +130,9 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { if (pageLink.isSearchPropagatedAlarms()) { fromPart.append(" and r.from_id in (:entity_ids)"); } else { - wherePart.append(" a.originator_id in (:entity_ids)"); + addAndIfNeeded(wherePart, addAnd); addAnd = true; + wherePart.append(" a.originator_id in (:entity_ids)"); } } else { fromPart.append(" left join (select * from (VALUES"); @@ -202,6 +207,31 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { return AlarmDataAdapter.createAlarmData(pageLink, rows, totalElements); } + private String buildPermissionsQuery(TenantId tenantId, CustomerId customerId, QueryContext ctx) { + StringBuilder permissionsQuery = new StringBuilder(); + ctx.addUuidParameter("permissions_tenant_id", tenantId.getId()); + permissionsQuery.append(" a.tenant_id = :permissions_tenant_id "); + if (customerId != null && !customerId.isNullUid()) { + ctx.addUuidParameter("permissions_customer_id", customerId.getId()); + ctx.addUuidParameter("permissions_device_customer_id", customerId.getId()); + ctx.addUuidParameter("permissions_asset_customer_id", customerId.getId()); + ctx.addUuidParameter("permissions_user_customer_id", customerId.getId()); + ctx.addUuidParameter("permissions_entity_view_customer_id", customerId.getId()); + permissionsQuery.append(" and ("); + permissionsQuery.append("(a.originator_type = '").append(EntityType.DEVICE.ordinal()).append("' and exists (select 1 from device cd where cd.id = a.originator_id and cd.customer_id = :permissions_device_customer_id))"); + permissionsQuery.append(" or "); + permissionsQuery.append("(a.originator_type = '").append(EntityType.ASSET.ordinal()).append("' and exists (select 1 from asset ca where ca.id = a.originator_id and ca.customer_id = :permissions_device_customer_id))"); + permissionsQuery.append(" or "); + permissionsQuery.append("(a.originator_type = '").append(EntityType.CUSTOMER.ordinal()).append("' and exists (select 1 from customer cc where cc.id = a.originator_id and cc.id = :permissions_customer_id))"); + permissionsQuery.append(" or "); + permissionsQuery.append("(a.originator_type = '").append(EntityType.USER.ordinal()).append("' and exists (select 1 from tb_user cu where cu.id = a.originator_id and cu.customer_id = :permissions_user_customer_id))"); + permissionsQuery.append(" or "); + permissionsQuery.append("(a.originator_type = '").append(EntityType.ENTITY_VIEW.ordinal()).append("' and exists (select 1 from entity_view cv where cv.id = a.originator_id and cv.customer_id = :permissions_entity_view_customer_id))"); + permissionsQuery.append(")"); + } + return permissionsQuery.toString(); + } + private Set toStatusSet(List statusList) { Set result = new HashSet<>(); for (AlarmSearchStatus searchStatus : statusList) { diff --git a/dao/src/main/resources/sql/schema-entities-idx.sql b/dao/src/main/resources/sql/schema-entities-idx.sql index 5b6df2080d..4d16d838d7 100644 --- a/dao/src/main/resources/sql/schema-entities-idx.sql +++ b/dao/src/main/resources/sql/schema-entities-idx.sql @@ -16,6 +16,8 @@ CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_type ON alarm(originator_id, type, start_ts DESC); +CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_time ON alarm(originator_id, created_time DESC); + CREATE INDEX IF NOT EXISTS idx_event_type_entity_id ON event(tenant_id, event_type, entity_type, entity_id); CREATE INDEX IF NOT EXISTS idx_relation_to_id ON relation(relation_type_group, to_type, to_id); diff --git a/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java b/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java index a6ef3935b0..db87deab93 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java +++ b/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java @@ -24,7 +24,7 @@ import java.util.Arrays; @RunWith(ClasspathSuite.class) @ClassnameFilters({ - "org.thingsboard.server.dao.service.sql.*SqlTest" + "org.thingsboard.server.dao.service.sql.AlarmServiceSqlTest" }) public class SqlDaoServiceTestSuite { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java index e02befcd84..ec0d10286b 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java @@ -20,6 +20,8 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmInfo; @@ -205,6 +207,63 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { Assert.assertEquals(created, alarms.getData().get(0)); } + @Test + public void testFindCustomerAlarm() throws ExecutionException, InterruptedException { + Customer customer = new Customer(); + customer.setTitle("TestCustomer"); + customer.setTenantId(tenantId); + customer = customerService.saveCustomer(customer); + + Device tenantDevice = new Device(); + tenantDevice.setName("TestTenantDevice"); + tenantDevice.setType("default"); + tenantDevice.setTenantId(tenantId); + tenantDevice = deviceService.saveDevice(tenantDevice); + + Device customerDevice = new Device(); + customerDevice.setName("TestCustomerDevice"); + customerDevice.setType("default"); + customerDevice.setTenantId(tenantId); + customerDevice.setCustomerId(customer.getId()); + customerDevice = deviceService.saveDevice(customerDevice); + + long ts = System.currentTimeMillis(); + Alarm tenantAlarm = Alarm.builder().tenantId(tenantId) + .originator(tenantDevice.getId()) + .type(TEST_ALARM) + .propagate(true) + .severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK) + .startTs(ts).build(); + tenantAlarm = alarmService.createOrUpdateAlarm(tenantAlarm); + + Alarm deviceAlarm = Alarm.builder().tenantId(tenantId) + .originator(customerDevice.getId()) + .type(TEST_ALARM) + .propagate(true) + .severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK) + .startTs(ts).build(); + deviceAlarm = alarmService.createOrUpdateAlarm(deviceAlarm); + + AlarmDataPageLink pageLink = new AlarmDataPageLink(); + pageLink.setPage(0); + pageLink.setPageSize(10); + pageLink.setSortOrder(new EntityDataSortOrder(new EntityKey(EntityKeyType.ALARM_FIELD, "createdTime"))); + + pageLink.setStartTs(0L); + pageLink.setEndTs(System.currentTimeMillis()); + pageLink.setSearchPropagatedAlarms(true); + pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING)); + pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE)); + + PageData tenantAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, new CustomerId(CustomerId.NULL_UUID), pageLink, Arrays.asList(tenantDevice.getId(), customerDevice.getId())); + Assert.assertEquals(2, tenantAlarms.getData().size()); + + PageData customerAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, customer.getId(), pageLink, Arrays.asList(tenantDevice.getId(), customerDevice.getId())); + Assert.assertEquals(1, customerAlarms.getData().size()); + Assert.assertEquals(deviceAlarm, customerAlarms.getData().get(0)); + + } + @Test public void testFindAlarmUsingAlarmDataQuery() throws ExecutionException, InterruptedException { AssetId parentId = new AssetId(Uuids.timeBased());