Commit f300c2349d0f53980b6a897e1e5eb8601a748835

Authored by Andrii Shvaika
1 parent b707ea09

Additional security checks for new alarm query

... ... @@ -87,7 +87,7 @@ public class AlarmController extends BaseController {
87 87 try {
88 88 alarm.setTenantId(getCurrentUser().getTenantId());
89 89
90   - checkEntity(alarm.getId(), alarm, Resource.ALARM);
  90 + checkEntity(alarm.getId(), alarm, Resource.ALARM);
91 91
92 92 Alarm savedAlarm = checkNotNull(alarmService.createOrUpdateAlarm(alarm));
93 93 logEntityAction(savedAlarm.getId(), savedAlarm,
... ...
... ... @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j;
19 19 import org.springframework.beans.factory.annotation.Autowired;
20 20 import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
21 21 import org.springframework.stereotype.Repository;
  22 +import org.thingsboard.server.common.data.EntityType;
22 23 import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
23 24 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
24 25 import org.thingsboard.server.common.data.alarm.AlarmStatus;
... ... @@ -63,19 +64,19 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
63 64 }
64 65
65 66 public static final String SELECT_ORIGINATOR_NAME = " CASE" +
66   - " WHEN a.originator_type = 0" +
  67 + " WHEN a.originator_type = "+ EntityType.TENANT.ordinal() +
67 68 " THEN (select title from tenant where id = a.originator_id)" +
68   - " WHEN a.originator_type = 1 " +
  69 + " WHEN a.originator_type = "+ EntityType.CUSTOMER.ordinal() +
69 70 " THEN (select title from customer where id = a.originator_id)" +
70   - " WHEN a.originator_type = 2" +
  71 + " WHEN a.originator_type = " + EntityType.USER.ordinal() +
71 72 " THEN (select CONCAT (first_name, ' ', last_name) from tb_user where id = a.originator_id)" +
72   - " WHEN a.originator_type = 3" +
  73 + " WHEN a.originator_type = " + EntityType.DASHBOARD.ordinal() +
73 74 " THEN (select title from dashboard where id = a.originator_id)" +
74   - " WHEN a.originator_type = 4" +
  75 + " WHEN a.originator_type = " + EntityType.ASSET.ordinal() +
75 76 " THEN (select name from asset where id = a.originator_id)" +
76   - " WHEN a.originator_type = 5" +
  77 + " WHEN a.originator_type = " + EntityType.DEVICE.ordinal() +
77 78 " THEN (select name from device where id = a.originator_id)" +
78   - " WHEN a.originator_type = 9" +
  79 + " WHEN a.originator_type = " + EntityType.ENTITY_VIEW.ordinal() +
79 80 " THEN (select name from entity_view where id = a.originator_id)" +
80 81 " END as originator_name";
81 82
... ... @@ -114,8 +115,11 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
114 115 if (pageLink.isSearchPropagatedAlarms()) {
115 116 selectPart.append(" r.from_id as entity_id ");
116 117 fromPart.append(JOIN_RELATIONS);
  118 + wherePart.append(buildPermissionsQuery(tenantId, customerId, ctx));
  119 + addAnd = true;
117 120 } else {
118 121 selectPart.append(" a.originator_id as entity_id ");
  122 + //No need to check permissions if we select by originator.
119 123 }
120 124 EntityDataSortOrder sortOrder = pageLink.getSortOrder();
121 125 if (sortOrder != null && sortOrder.getKey().getType().equals(EntityKeyType.ALARM_FIELD)) {
... ... @@ -126,8 +130,9 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
126 130 if (pageLink.isSearchPropagatedAlarms()) {
127 131 fromPart.append(" and r.from_id in (:entity_ids)");
128 132 } else {
129   - wherePart.append(" a.originator_id in (:entity_ids)");
  133 + addAndIfNeeded(wherePart, addAnd);
130 134 addAnd = true;
  135 + wherePart.append(" a.originator_id in (:entity_ids)");
131 136 }
132 137 } else {
133 138 fromPart.append(" left join (select * from (VALUES");
... ... @@ -202,6 +207,31 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
202 207 return AlarmDataAdapter.createAlarmData(pageLink, rows, totalElements);
203 208 }
204 209
  210 + private String buildPermissionsQuery(TenantId tenantId, CustomerId customerId, QueryContext ctx) {
  211 + StringBuilder permissionsQuery = new StringBuilder();
  212 + ctx.addUuidParameter("permissions_tenant_id", tenantId.getId());
  213 + permissionsQuery.append(" a.tenant_id = :permissions_tenant_id ");
  214 + if (customerId != null && !customerId.isNullUid()) {
  215 + ctx.addUuidParameter("permissions_customer_id", customerId.getId());
  216 + ctx.addUuidParameter("permissions_device_customer_id", customerId.getId());
  217 + ctx.addUuidParameter("permissions_asset_customer_id", customerId.getId());
  218 + ctx.addUuidParameter("permissions_user_customer_id", customerId.getId());
  219 + ctx.addUuidParameter("permissions_entity_view_customer_id", customerId.getId());
  220 + permissionsQuery.append(" and (");
  221 + 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))");
  222 + permissionsQuery.append(" or ");
  223 + 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))");
  224 + permissionsQuery.append(" or ");
  225 + 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))");
  226 + permissionsQuery.append(" or ");
  227 + 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))");
  228 + permissionsQuery.append(" or ");
  229 + 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))");
  230 + permissionsQuery.append(")");
  231 + }
  232 + return permissionsQuery.toString();
  233 + }
  234 +
205 235 private Set<AlarmStatus> toStatusSet(List<AlarmSearchStatus> statusList) {
206 236 Set<AlarmStatus> result = new HashSet<>();
207 237 for (AlarmSearchStatus searchStatus : statusList) {
... ...
... ... @@ -16,6 +16,8 @@
16 16
17 17 CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_type ON alarm(originator_id, type, start_ts DESC);
18 18
  19 +CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_time ON alarm(originator_id, created_time DESC);
  20 +
19 21 CREATE INDEX IF NOT EXISTS idx_event_type_entity_id ON event(tenant_id, event_type, entity_type, entity_id);
20 22
21 23 CREATE INDEX IF NOT EXISTS idx_relation_to_id ON relation(relation_type_group, to_type, to_id);
... ...
... ... @@ -24,7 +24,7 @@ import java.util.Arrays;
24 24
25 25 @RunWith(ClasspathSuite.class)
26 26 @ClassnameFilters({
27   - "org.thingsboard.server.dao.service.sql.*SqlTest"
  27 + "org.thingsboard.server.dao.service.sql.AlarmServiceSqlTest"
28 28 })
29 29 public class SqlDaoServiceTestSuite {
30 30
... ...
... ... @@ -20,6 +20,8 @@ import org.junit.After;
20 20 import org.junit.Assert;
21 21 import org.junit.Before;
22 22 import org.junit.Test;
  23 +import org.thingsboard.server.common.data.Customer;
  24 +import org.thingsboard.server.common.data.Device;
23 25 import org.thingsboard.server.common.data.Tenant;
24 26 import org.thingsboard.server.common.data.alarm.Alarm;
25 27 import org.thingsboard.server.common.data.alarm.AlarmInfo;
... ... @@ -206,6 +208,63 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
206 208 }
207 209
208 210 @Test
  211 + public void testFindCustomerAlarm() throws ExecutionException, InterruptedException {
  212 + Customer customer = new Customer();
  213 + customer.setTitle("TestCustomer");
  214 + customer.setTenantId(tenantId);
  215 + customer = customerService.saveCustomer(customer);
  216 +
  217 + Device tenantDevice = new Device();
  218 + tenantDevice.setName("TestTenantDevice");
  219 + tenantDevice.setType("default");
  220 + tenantDevice.setTenantId(tenantId);
  221 + tenantDevice = deviceService.saveDevice(tenantDevice);
  222 +
  223 + Device customerDevice = new Device();
  224 + customerDevice.setName("TestCustomerDevice");
  225 + customerDevice.setType("default");
  226 + customerDevice.setTenantId(tenantId);
  227 + customerDevice.setCustomerId(customer.getId());
  228 + customerDevice = deviceService.saveDevice(customerDevice);
  229 +
  230 + long ts = System.currentTimeMillis();
  231 + Alarm tenantAlarm = Alarm.builder().tenantId(tenantId)
  232 + .originator(tenantDevice.getId())
  233 + .type(TEST_ALARM)
  234 + .propagate(true)
  235 + .severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK)
  236 + .startTs(ts).build();
  237 + tenantAlarm = alarmService.createOrUpdateAlarm(tenantAlarm);
  238 +
  239 + Alarm deviceAlarm = Alarm.builder().tenantId(tenantId)
  240 + .originator(customerDevice.getId())
  241 + .type(TEST_ALARM)
  242 + .propagate(true)
  243 + .severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK)
  244 + .startTs(ts).build();
  245 + deviceAlarm = alarmService.createOrUpdateAlarm(deviceAlarm);
  246 +
  247 + AlarmDataPageLink pageLink = new AlarmDataPageLink();
  248 + pageLink.setPage(0);
  249 + pageLink.setPageSize(10);
  250 + pageLink.setSortOrder(new EntityDataSortOrder(new EntityKey(EntityKeyType.ALARM_FIELD, "createdTime")));
  251 +
  252 + pageLink.setStartTs(0L);
  253 + pageLink.setEndTs(System.currentTimeMillis());
  254 + pageLink.setSearchPropagatedAlarms(true);
  255 + pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
  256 + pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE));
  257 +
  258 + PageData<AlarmData> tenantAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, new CustomerId(CustomerId.NULL_UUID), pageLink, Arrays.asList(tenantDevice.getId(), customerDevice.getId()));
  259 + Assert.assertEquals(2, tenantAlarms.getData().size());
  260 +
  261 + PageData<AlarmData> customerAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, customer.getId(), pageLink, Arrays.asList(tenantDevice.getId(), customerDevice.getId()));
  262 + Assert.assertEquals(1, customerAlarms.getData().size());
  263 + Assert.assertEquals(deviceAlarm, customerAlarms.getData().get(0));
  264 +
  265 + }
  266 +
  267 + @Test
209 268 public void testFindAlarmUsingAlarmDataQuery() throws ExecutionException, InterruptedException {
210 269 AssetId parentId = new AssetId(Uuids.timeBased());
211 270 AssetId childId = new AssetId(Uuids.timeBased());
... ...