diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java index 41871e3dae..23ba6fd19e 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java @@ -31,7 +31,7 @@ public class CustomerUserPermissions extends AbstractPermissions { public CustomerUserPermissions() { super(); - put(Resource.ALARM, TenantAdminPermissions.tenantEntityPermissionChecker); + put(Resource.ALARM, customerAlarmPermissionChecker); put(Resource.ASSET, customerEntityPermissionChecker); put(Resource.DEVICE, customerEntityPermissionChecker); put(Resource.CUSTOMER, customerPermissionChecker); @@ -44,6 +44,19 @@ public class CustomerUserPermissions extends AbstractPermissions { put(Resource.RPC, rpcPermissionChecker); } + private static final PermissionChecker customerAlarmPermissionChecker = new PermissionChecker() { + @Override + public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { + if (!user.getTenantId().equals(entity.getTenantId())) { + return false; + } + if (!(entity instanceof HasCustomerId)) { + return false; + } + return user.getCustomerId().equals(((HasCustomerId) entity).getCustomerId()); + } + }; + private static final PermissionChecker customerEntityPermissionChecker = new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_CREDENTIALS, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY, Operation.RPC_CALL, Operation.CLAIM_DEVICES, @@ -62,10 +75,7 @@ public class CustomerUserPermissions extends AbstractPermissions { if (!(entity instanceof HasCustomerId)) { return false; } - if (!operation.equals(Operation.CLAIM_DEVICES) && !user.getCustomerId().equals(((HasCustomerId) entity).getCustomerId())) { - return false; - } - return true; + return operation.equals(Operation.CLAIM_DEVICES) || user.getCustomerId().equals(((HasCustomerId) entity).getCustomerId()); } }; @@ -78,10 +88,7 @@ public class CustomerUserPermissions extends AbstractPermissions { if (!super.hasPermission(user, operation, entityId, entity)) { return false; } - if (!user.getCustomerId().equals(entityId)) { - return false; - } - return true; + return user.getCustomerId().equals(entityId); } }; @@ -98,10 +105,7 @@ public class CustomerUserPermissions extends AbstractPermissions { if (!user.getTenantId().equals(dashboard.getTenantId())) { return false; } - if (!dashboard.isAssignedToCustomer(user.getCustomerId())) { - return false; - } - return true; + return dashboard.isAssignedToCustomer(user.getCustomerId()); } }; @@ -113,10 +117,7 @@ public class CustomerUserPermissions extends AbstractPermissions { if (!Authority.CUSTOMER_USER.equals(userEntity.getAuthority())) { return false; } - if (!user.getId().equals(userId)) { - return false; - } - return true; + return user.getId().equals(userId); } }; @@ -132,10 +133,7 @@ public class CustomerUserPermissions extends AbstractPermissions { if (entity.getTenantId() == null || entity.getTenantId().isNullUid()) { return true; } - if (!user.getTenantId().equals(entity.getTenantId())) { - return false; - } - return true; + return user.getTenantId().equals(entity.getTenantId()); } }; @@ -151,10 +149,7 @@ public class CustomerUserPermissions extends AbstractPermissions { if (entity.getTenantId() == null || entity.getTenantId().isNullUid()) { return true; } - if (!user.getTenantId().equals(entity.getTenantId())) { - return false; - } - return true; + return user.getTenantId().equals(entity.getTenantId()); } }; } diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index 6625bdefc7..cc561e31a8 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -64,6 +64,7 @@ import org.thingsboard.server.common.data.device.profile.MqttTopics; import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -124,6 +125,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { protected String username; protected TenantId tenantId; + protected CustomerId customerId; @SuppressWarnings("rawtypes") private HttpMessageConverter mappingJackson2HttpMessageConverter; @@ -192,6 +194,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { customer.setTitle("Customer"); customer.setTenantId(tenantId); Customer savedCustomer = doPost("/api/customer", customer, Customer.class); + customerId = savedCustomer.getId(); User customerUser = new User(); customerUser.setAuthority(Authority.CUSTOMER_USER); @@ -254,9 +257,14 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { } private Tenant savedDifferentTenant; + private Customer savedDifferentCustomer; protected void loginDifferentTenant() throws Exception { loginSysAdmin(); + if (savedDifferentTenant != null) { + deleteDifferentTenant(); + } + Tenant tenant = new Tenant(); tenant.setTitle("Different tenant"); savedDifferentTenant = doPost("/api/tenant", tenant, Tenant.class); @@ -269,10 +277,27 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { createUserAndLogin(differentTenantAdmin, "testPassword"); } + protected void loginDifferentCustomer() throws Exception { + loginTenantAdmin(); + Customer customer = new Customer(); + customer.setTitle("Different customer"); + savedDifferentCustomer = doPost("/api/customer", customer, Customer.class); + Assert.assertNotNull(savedDifferentCustomer); + User differentCustomerUser = new User(); + differentCustomerUser.setAuthority(Authority.CUSTOMER_USER); + differentCustomerUser.setTenantId(tenantId); + differentCustomerUser.setCustomerId(savedDifferentCustomer.getId()); + differentCustomerUser.setEmail("different_customer@thingsboard.org"); + + createUserAndLogin(differentCustomerUser, "testPassword"); + } + protected void deleteDifferentTenant() throws Exception { - loginSysAdmin(); - doDelete("/api/tenant/" + savedDifferentTenant.getId().getId().toString()) - .andExpect(status().isOk()); + if (savedDifferentTenant != null) { + loginSysAdmin(); + doDelete("/api/tenant/" + savedDifferentTenant.getId().getId().toString()) + .andExpect(status().isOk()); + } } protected User createUserAndLogin(User user, String password) throws Exception { diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java new file mode 100644 index 0000000000..ce9b6916fc --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java @@ -0,0 +1,230 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.controller; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.alarm.AlarmStatus; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +public abstract class BaseAlarmControllerTest extends AbstractControllerTest { + + public static final String TEST_ALARM_TYPE = "Test"; + + protected Device customerDevice; + + @Before + public void setup() throws Exception { + loginTenantAdmin(); + + Device device = new Device(); + device.setTenantId(tenantId); + device.setName("Test device"); + device.setLabel("Label"); + device.setType("Type"); + device.setCustomerId(customerId); + customerDevice = doPost("/api/device", device, Device.class); + + logout(); + } + + @After + public void teardown() throws Exception { + loginSysAdmin(); + deleteDifferentTenant(); + } + + @Test + public void testCreateAlarmViaCustomer() throws Exception { + loginCustomerUser(); + createAlarm(TEST_ALARM_TYPE); + logout(); + } + + @Test + public void testCreateAlarmViaTenant() throws Exception { + loginTenantAdmin(); + createAlarm(TEST_ALARM_TYPE); + logout(); + } + + @Test + public void testUpdateAlarmViaCustomer() throws Exception { + loginCustomerUser(); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + alarm.setSeverity(AlarmSeverity.MAJOR); + Alarm updatedAlarm = doPost("/api/alarm", alarm, Alarm.class); + Assert.assertNotNull(updatedAlarm); + Assert.assertEquals(AlarmSeverity.MAJOR, updatedAlarm.getSeverity()); + logout(); + } + + @Test + public void testUpdateAlarmViaTenant() throws Exception { + loginTenantAdmin(); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + alarm.setSeverity(AlarmSeverity.MAJOR); + Alarm updatedAlarm = doPost("/api/alarm", alarm, Alarm.class); + Assert.assertNotNull(updatedAlarm); + Assert.assertEquals(AlarmSeverity.MAJOR, updatedAlarm.getSeverity()); + logout(); + } + + @Test + public void testUpdateAlarmViaDifferentTenant() throws Exception { + loginTenantAdmin(); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + alarm.setSeverity(AlarmSeverity.MAJOR); + loginDifferentTenant(); + doPost("/api/alarm", alarm).andExpect(status().isForbidden()); + logout(); + } + + @Test + public void testUpdateAlarmViaDifferentCustomer() throws Exception { + loginCustomerUser(); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + loginDifferentCustomer(); + alarm.setSeverity(AlarmSeverity.MAJOR); + doPost("/api/alarm", alarm).andExpect(status().isForbidden()); + logout(); + } + + @Test + public void testDeleteAlarmViaCustomer() throws Exception { + loginCustomerUser(); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isOk()); + logout(); + } + + @Test + public void testDeleteAlarmViaTenant() throws Exception { + loginTenantAdmin(); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isOk()); + logout(); + } + + @Test + public void testDeleteAlarmViaDifferentTenant() throws Exception { + loginTenantAdmin(); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + loginDifferentTenant(); + doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isForbidden()); + logout(); + } + + @Test + public void testDeleteAlarmViaAnotherCustomer() throws Exception { + loginCustomerUser(); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + loginDifferentCustomer(); + doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isForbidden()); + logout(); + } + + @Test + public void testClearAlarmViaCustomer() throws Exception { + loginCustomerUser(); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isOk()); + Alarm foundAlarm = doGet("/api/alarm/" + alarm.getId(), Alarm.class); + Assert.assertNotNull(foundAlarm); + Assert.assertEquals(AlarmStatus.CLEARED_UNACK, foundAlarm.getStatus()); + logout(); + } + + @Test + public void testClearAlarmViaTenant() throws Exception { + loginTenantAdmin(); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isOk()); + Alarm foundAlarm = doGet("/api/alarm/" + alarm.getId(), Alarm.class); + Assert.assertNotNull(foundAlarm); + Assert.assertEquals(AlarmStatus.CLEARED_UNACK, foundAlarm.getStatus()); + logout(); + } + + @Test + public void testAcknowledgeAlarmViaCustomer() throws Exception { + loginCustomerUser(); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + doPost("/api/alarm/" + alarm.getId() + "/ack").andExpect(status().isOk()); + Alarm foundAlarm = doGet("/api/alarm/" + alarm.getId(), Alarm.class); + Assert.assertNotNull(foundAlarm); + Assert.assertEquals(AlarmStatus.ACTIVE_ACK, foundAlarm.getStatus()); + logout(); + } + + @Test + public void testClearAlarmViaDifferentCustomer() throws Exception { + loginCustomerUser(); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + loginDifferentCustomer(); + doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isForbidden()); + logout(); + } + + @Test + public void testClearAlarmViaDifferentTenant() throws Exception { + loginTenantAdmin(); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + loginDifferentTenant(); + doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isForbidden()); + logout(); + } + + @Test + public void testAcknowledgeAlarmViaDifferentCustomer() throws Exception { + loginCustomerUser(); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + loginDifferentCustomer(); + doPost("/api/alarm/" + alarm.getId() + "/ack").andExpect(status().isForbidden()); + logout(); + } + + @Test + public void testAcknowledgeAlarmViaDifferentTenant() throws Exception { + loginTenantAdmin(); + Alarm alarm = createAlarm(TEST_ALARM_TYPE); + loginDifferentTenant(); + doPost("/api/alarm/" + alarm.getId() + "/ack").andExpect(status().isForbidden()); + logout(); + } + + private Alarm createAlarm(String type) throws Exception { + Alarm alarm = Alarm.builder() + .tenantId(tenantId) + .customerId(customerId) + .originator(customerDevice.getId()) + .status(AlarmStatus.ACTIVE_UNACK) + .severity(AlarmSeverity.CRITICAL) + .type(type) + .build(); + + alarm = doPost("/api/alarm", alarm, Alarm.class); + Assert.assertNotNull(alarm); + + return alarm; + } +} diff --git a/application/src/test/java/org/thingsboard/server/controller/sql/AlarmControllerSqlTest.java b/application/src/test/java/org/thingsboard/server/controller/sql/AlarmControllerSqlTest.java new file mode 100644 index 0000000000..bba390448c --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/controller/sql/AlarmControllerSqlTest.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.controller.sql; + +import org.thingsboard.server.controller.BaseAlarmControllerTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +@DaoSqlTest +public class AlarmControllerSqlTest extends BaseAlarmControllerTest { +}