Browse Source

update alarm comment moderation logic: delete is allowed for all users with alarm WRITE permission, edit - only for authors

pull/15715/head
dashevchenko 2 months ago
parent
commit
0276d66d72
  1. 16
      application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java
  2. 2
      application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java
  3. 23
      application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
  4. 41
      application/src/test/java/org/thingsboard/server/controller/AlarmCommentControllerTest.java

16
application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java

@ -82,7 +82,10 @@ public class AlarmCommentController extends BaseController {
SecurityUser currentUser = getCurrentUser();
if (alarmComment.getId() != null) {
AlarmComment existingAlarmComment = checkAlarmCommentId(alarmComment.getId(), alarmId);
checkUserCommentOwnership(existingAlarmComment, "edit", currentUser);
if (existingAlarmComment.getUserId() != null && !existingAlarmComment.getUserId().equals(currentUser.getId())) {
throw new ThingsboardException("User is not allowed to edit other user's comment",
ThingsboardErrorCode.PERMISSION_DENIED);
}
}
alarmComment.setAlarmId(alarmId);
alarmComment.setType(AlarmCommentType.OTHER);
@ -101,7 +104,6 @@ public class AlarmCommentController extends BaseController {
AlarmCommentId alarmCommentId = new AlarmCommentId(toUUID(strCommentId));
AlarmComment alarmComment = checkAlarmCommentId(alarmCommentId, alarmId);
SecurityUser currentUser = getCurrentUser();
checkUserCommentOwnership(alarmComment, "delete", currentUser);
tbAlarmCommentService.deleteAlarmComment(alarm, alarmComment, currentUser);
}
@ -129,14 +131,4 @@ public class AlarmCommentController extends BaseController {
return checkNotNull(alarmCommentService.findAlarmComments(alarm.getTenantId(), alarmId, pageLink));
}
private void checkUserCommentOwnership(AlarmComment alarmComment, String action, SecurityUser securityUser) throws ThingsboardException {
if (securityUser.isTenantAdmin()) {
return;
}
if (alarmComment.getUserId() != null && !alarmComment.getUserId().equals(securityUser.getId())) {
throw new ThingsboardException("User is not allowed to " + action + " other user's comment",
ThingsboardErrorCode.PERMISSION_DENIED);
}
}
}

2
application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java

@ -60,7 +60,7 @@ public class DefaultTbAlarmCommentService extends AbstractTbEntityService implem
alarmComment.setType(AlarmCommentType.SYSTEM);
alarmComment.setUserId(null);
alarmComment.setComment(JacksonUtil.newObjectNode().put("text",
String.format("User %s deleted his comment",
String.format("Comment was deleted by user %s",
(user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName())));
AlarmComment savedAlarmComment = checkNotNull(alarmCommentService.saveAlarmComment(alarm.getTenantId(), alarmComment));
logEntityActionService.logEntityAction(alarm.getTenantId(), alarm.getId(), alarm, alarm.getCustomerId(), ActionType.DELETED_COMMENT, user, savedAlarmComment);

23
application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java

@ -210,6 +210,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
private static final String DIFFERENT_TENANT_ADMIN_PASSWORD = "difftenant";
protected static final String CUSTOMER_USER_EMAIL = "testcustomer@thingsboard.org";
protected static final String SECOND_CUSTOMER_USER_EMAIL = "testsecondcustomer@thingsboard.org";
private static final String CUSTOMER_USER_PASSWORD = "customer";
protected static final String DIFFERENT_CUSTOMER_USER_EMAIL = "testdifferentcustomer@thingsboard.org";
@ -247,6 +248,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
protected CustomerId differentTenantCustomerId;
protected UserId customerUserId;
protected UserId secondCustomerUserId;
protected UserId differentCustomerUserId;
protected UserId differentTenantCustomerUserId;
@ -372,9 +374,17 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
customerUser.setCustomerId(savedCustomer.getId());
customerUser.setEmail(CUSTOMER_USER_EMAIL);
customerUser = createUserAndLogin(customerUser, CUSTOMER_USER_PASSWORD);
customerUser = createUserAndActivate(customerUser, CUSTOMER_USER_PASSWORD);
customerUserId = customerUser.getId();
User secondCustomerUser = new User();
secondCustomerUser.setAuthority(Authority.CUSTOMER_USER);
secondCustomerUser.setTenantId(tenantId);
secondCustomerUser.setCustomerId(customerId);
secondCustomerUser.setEmail(SECOND_CUSTOMER_USER_EMAIL);
secondCustomerUser = createUserAndActivate(secondCustomerUser, CUSTOMER_USER_PASSWORD);
secondCustomerUserId = secondCustomerUser.getId();
resetTokens();
log.debug("Executed web test setup");
@ -472,6 +482,10 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
login(CUSTOMER_USER_EMAIL, CUSTOMER_USER_PASSWORD);
}
protected void loginSecondCustomerUser() throws Exception {
login(SECOND_CUSTOMER_USER_EMAIL, CUSTOMER_USER_PASSWORD);
}
protected void loginUser(String userName, String password) throws Exception {
login(userName, password);
}
@ -586,6 +600,13 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
return savedUser;
}
protected User createUserAndActivate(User user, String password) throws Exception {
User savedUser = doPost("/api/user", user, User.class);
JsonNode activateRequest = getActivateRequest(password);
doPost("/api/noauth/activate", activateRequest).andExpect(status().isOk());
return savedUser;
}
protected User createUser(User user, String password) throws Exception {
User savedUser = doPost("/api/user", user, User.class);
JsonNode activateRequest = getActivateRequest(password);

41
application/src/test/java/org/thingsboard/server/controller/AlarmCommentControllerTest.java

@ -161,25 +161,22 @@ public class AlarmCommentControllerTest extends AbstractControllerTest {
}
@Test
public void testUpdateOthersAlarmCommentByTenantAdmin() throws Exception {
// Tenant admins may moderate comments authored by other users — the ownership rule
// applies only to non-admin users, so a tenant admin can edit someone else's comment.
public void testEditOthersAlarmCommentIsProhibited() throws Exception {
loginCustomerUser();
AlarmComment alarmComment = createAlarmComment(alarm.getId());
loginTenantAdmin();
Mockito.reset(tbClusterService, auditLogService);
JsonNode newComment = JacksonUtil.newObjectNode().set("text", new TextNode("Tenant rewrite"));
JsonNode newComment = JacksonUtil.newObjectNode().set("text", new TextNode("Second customer rewrite"));
alarmComment.setComment(newComment);
AlarmComment updatedAlarmComment = saveAlarmComment(alarm.getId(), alarmComment);
Assert.assertNotNull(updatedAlarmComment);
Assert.assertEquals(newComment.get("text"), updatedAlarmComment.getComment().get("text"));
Assert.assertEquals("true", updatedAlarmComment.getComment().get("edited").asText());
Assert.assertNotNull(updatedAlarmComment.getComment().get("editedOn"));
loginSecondCustomerUser();
doPost("/api/alarm/" + alarm.getId() + "/comment", alarmComment)
.andExpect(status().isForbidden())
.andExpect(statusReason(containsString("User is not allowed to edit other user's comment")));
testLogEntityActionEntityEqClass(alarm, alarm.getId(), tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.UPDATED_COMMENT, 1, updatedAlarmComment);
loginTenantAdmin();
doPost("/api/alarm/" + alarm.getId() + "/comment", alarmComment)
.andExpect(status().isForbidden())
.andExpect(statusReason(containsString("User is not allowed to edit other user's comment")));
}
@Test
@ -231,20 +228,18 @@ public class AlarmCommentControllerTest extends AbstractControllerTest {
AlarmComment expectedAlarmComment = AlarmComment.builder()
.alarmId(alarm.getId())
.type(AlarmCommentType.SYSTEM)
.comment(JacksonUtil.newObjectNode().put("text", String.format("User %s deleted his comment",
.comment(JacksonUtil.newObjectNode().put("text", String.format("Comment was deleted by user %s",
CUSTOMER_USER_EMAIL)))
.build();
testLogEntityActionEntityEqClass(alarm, alarm.getId(), tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.DELETED_COMMENT, 1, expectedAlarmComment);
}
@Test
public void testDeleteOthersAlarmCommentByTenantAdmin() throws Exception {
// Tenant admins may moderate comments authored by other users — the ownership rule
// applies only to non-admin users, so a tenant admin can delete someone else's comment.
public void testDeleteOthersAlarmCommentIsAllowedForUserWithAlarmWritePermission() throws Exception {
loginCustomerUser();
AlarmComment alarmComment = createAlarmComment(alarm.getId());
loginTenantAdmin();
loginSecondCustomerUser();
Mockito.reset(tbClusterService, auditLogService);
doDelete("/api/alarm/" + alarm.getId() + "/comment/" + alarmComment.getId())
@ -253,10 +248,10 @@ public class AlarmCommentControllerTest extends AbstractControllerTest {
AlarmComment expectedAlarmComment = AlarmComment.builder()
.alarmId(alarm.getId())
.type(AlarmCommentType.SYSTEM)
.comment(JacksonUtil.newObjectNode().put("text", String.format("User %s deleted his comment",
TENANT_ADMIN_EMAIL)))
.comment(JacksonUtil.newObjectNode().put("text", String.format("Comment was deleted by user %s",
SECOND_CUSTOMER_USER_EMAIL)))
.build();
testLogEntityActionEntityEqClass(alarm, alarm.getId(), tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.DELETED_COMMENT, 1, expectedAlarmComment);
testLogEntityActionEntityEqClass(alarm, alarm.getId(), tenantId, customerId, secondCustomerUserId, SECOND_CUSTOMER_USER_EMAIL, ActionType.DELETED_COMMENT, 1, expectedAlarmComment);
}
@Test
@ -278,13 +273,13 @@ public class AlarmCommentControllerTest extends AbstractControllerTest {
assertThat(systemComment.getId()).isEqualTo(alarmComment.getId());
assertThat(systemComment.getType()).isEqualTo(AlarmCommentType.SYSTEM);
assertThat(systemComment.getComment().get("text").asText()).isEqualTo(String.format("User %s deleted his comment",
assertThat(systemComment.getComment().get("text").asText()).isEqualTo(String.format("Comment was deleted by user %s",
TENANT_ADMIN_EMAIL));
AlarmComment expectedAlarmComment = AlarmComment.builder()
.alarmId(alarm.getId())
.type(AlarmCommentType.SYSTEM)
.comment(JacksonUtil.newObjectNode().put("text", String.format("User %s deleted his comment",
.comment(JacksonUtil.newObjectNode().put("text", String.format("Comment was deleted by user %s",
TENANT_ADMIN_EMAIL)))
.build();
testLogEntityActionEntityEqClass(alarm, alarm.getId(), tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.DELETED_COMMENT, 1, expectedAlarmComment);

Loading…
Cancel
Save