Browse Source

Additional security checks for new alarm query

pull/3053/head
Andrii Shvaika 6 years ago
parent
commit
f300c2349d
  1. 2
      application/src/main/java/org/thingsboard/server/controller/AlarmController.java
  2. 46
      dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java
  3. 2
      dao/src/main/resources/sql/schema-entities-idx.sql
  4. 2
      dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java
  5. 59
      dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java

2
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,

46
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<AlarmStatus> toStatusSet(List<AlarmSearchStatus> statusList) {
Set<AlarmStatus> result = new HashSet<>();
for (AlarmSearchStatus searchStatus : statusList) {

2
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);

2
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 {

59
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<AlarmData> tenantAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, new CustomerId(CustomerId.NULL_UUID), pageLink, Arrays.asList(tenantDevice.getId(), customerDevice.getId()));
Assert.assertEquals(2, tenantAlarms.getData().size());
PageData<AlarmData> 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());

Loading…
Cancel
Save