Merge pull request #8256 from imbeacon/fix/alarm-assignment-user-issues
Added getting user list for alarm assignment and ability to assign alarms for customer users
This commit is contained in:
commit
8769911694
@ -42,8 +42,10 @@ import org.thingsboard.server.common.data.DashboardInfo;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
import org.thingsboard.server.common.data.UserEmailInfo;
|
||||
import org.thingsboard.server.common.data.alarm.Alarm;
|
||||
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
|
||||
import org.thingsboard.server.common.data.exception.ThingsboardException;
|
||||
import org.thingsboard.server.common.data.id.AlarmId;
|
||||
import org.thingsboard.server.common.data.id.CustomerId;
|
||||
import org.thingsboard.server.common.data.id.DashboardId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
@ -63,6 +65,7 @@ import org.thingsboard.server.common.data.settings.UserDashboardsInfo;
|
||||
import org.thingsboard.server.common.data.settings.UserSettings;
|
||||
import org.thingsboard.server.common.data.security.event.UserCredentialsInvalidationEvent;
|
||||
import org.thingsboard.server.common.data.security.model.JwtPair;
|
||||
import org.thingsboard.server.dao.entity.EntityService;
|
||||
import org.thingsboard.server.common.data.settings.UserSettingsType;
|
||||
import org.thingsboard.server.queue.util.TbCoreComponent;
|
||||
import org.thingsboard.server.service.entitiy.user.TbUserService;
|
||||
@ -75,11 +78,14 @@ import org.thingsboard.server.service.security.permission.Resource;
|
||||
import org.thingsboard.server.service.security.system.SystemSecurityService;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.thingsboard.server.common.data.query.EntityKeyType.ENTITY_FIELD;
|
||||
import static org.thingsboard.server.controller.ControllerConstants.ALARM_ID_PARAM_DESCRIPTION;
|
||||
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID;
|
||||
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION;
|
||||
import static org.thingsboard.server.controller.ControllerConstants.DASHBOARD_ID_PARAM_DESCRIPTION;
|
||||
@ -125,6 +131,9 @@ public class UserController extends BaseController {
|
||||
@Autowired
|
||||
private EntityQueryService entityQueryService;
|
||||
|
||||
@Autowired
|
||||
private EntityService entityService;
|
||||
|
||||
@ApiOperation(value = "Get User (getUserById)",
|
||||
notes = "Fetch the User object based on the provided User Id. " +
|
||||
"If the user has the authority of 'SYS_ADMIN', the server does not perform additional checks. " +
|
||||
@ -444,6 +453,54 @@ public class UserController extends BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
@ApiOperation(value = "Get usersForAssign (getUsersForAssign)",
|
||||
notes = "Returns page of user data objects that can be assigned to provided alarmId. " +
|
||||
"Search is been executed by email, firstName and lastName fields. " +
|
||||
PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
|
||||
@RequestMapping(value = "/users/assign/{alarmId}", params = {"pageSize", "page"}, method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public PageData<UserEmailInfo> getUsersForAssign(
|
||||
@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION, required = true)
|
||||
@PathVariable("alarmId") String strAlarmId,
|
||||
@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
|
||||
@RequestParam int pageSize,
|
||||
@ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
|
||||
@RequestParam int page,
|
||||
@ApiParam(value = USER_TEXT_SEARCH_DESCRIPTION)
|
||||
@RequestParam(required = false) String textSearch,
|
||||
@ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = USER_SORT_PROPERTY_ALLOWABLE_VALUES)
|
||||
@RequestParam(required = false) String sortProperty,
|
||||
@ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
|
||||
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
|
||||
try {
|
||||
checkParameter("alarmId", strAlarmId);
|
||||
AlarmId alarmEntityId = new AlarmId(toUUID(strAlarmId));
|
||||
Alarm alarm = checkAlarmId(alarmEntityId, Operation.READ);
|
||||
SecurityUser currentUser = getCurrentUser();
|
||||
TenantId tenantId = currentUser.getTenantId();
|
||||
CustomerId originatorCustomerId = entityService.fetchEntityCustomerId(tenantId, alarm.getOriginator()).get();
|
||||
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
|
||||
PageData<User> pageData;
|
||||
if (Authority.TENANT_ADMIN.equals(currentUser.getAuthority())) {
|
||||
if (alarm.getCustomerId() == null) {
|
||||
pageData = userService.findTenantAdmins(tenantId, pageLink);
|
||||
} else {
|
||||
ArrayList<CustomerId> customerIds = new ArrayList<>(Collections.singletonList(new CustomerId(CustomerId.NULL_UUID)));
|
||||
if (!CustomerId.NULL_UUID.equals(originatorCustomerId.getId())) {
|
||||
customerIds.add(originatorCustomerId);
|
||||
}
|
||||
pageData = userService.findUsersByCustomerIds(tenantId, customerIds, pageLink);
|
||||
}
|
||||
} else {
|
||||
pageData = userService.findCustomerUsers(tenantId, alarm.getCustomerId(), pageLink);
|
||||
}
|
||||
return pageData.mapData(user -> new UserEmailInfo(user.getId(), user.getEmail(), user.getFirstName(), user.getLastName()));
|
||||
} catch (Exception e) {
|
||||
throw handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiOperation(value = "Save user settings (saveUserSettings)",
|
||||
notes = "Save user settings represented in json format for authorized user. ")
|
||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
|
||||
|
||||
@ -119,6 +119,15 @@ public class CustomerUserPermissions extends AbstractPermissions {
|
||||
if (!Authority.CUSTOMER_USER.equals(userEntity.getAuthority())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!user.getCustomerId().equals(userEntity.getCustomerId())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Operation.READ.equals(operation)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return user.getId().equals(userId);
|
||||
}
|
||||
|
||||
|
||||
@ -810,6 +810,15 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
|
||||
|
||||
}
|
||||
|
||||
public class EntityIdComparator<D extends EntityId> implements Comparator<D> {
|
||||
|
||||
@Override
|
||||
public int compare(D o1, D o2) {
|
||||
return o1.getId().compareTo(o2.getId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static <T> ResultMatcher statusReason(Matcher<T> matcher) {
|
||||
return jsonPath("$.message", matcher);
|
||||
}
|
||||
|
||||
@ -32,11 +32,14 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.web.servlet.ResultActions;
|
||||
import org.thingsboard.server.common.data.Customer;
|
||||
import org.thingsboard.server.common.data.Device;
|
||||
import org.thingsboard.server.common.data.Dashboard;
|
||||
import org.thingsboard.server.common.data.StringUtils;
|
||||
import org.thingsboard.server.common.data.Tenant;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
import org.thingsboard.server.common.data.UserEmailInfo;
|
||||
import org.thingsboard.server.common.data.alarm.Alarm;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
||||
import org.thingsboard.server.common.data.audit.ActionType;
|
||||
import org.thingsboard.server.common.data.id.CustomerId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
@ -57,7 +60,6 @@ import java.util.stream.Collectors;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
@ -70,6 +72,8 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
|
||||
private IdComparator<User> idComparator = new IdComparator<>();
|
||||
private IdComparator<UserEmailInfo> userDataIdComparator = new IdComparator<>();
|
||||
|
||||
private EntityIdComparator<UserId> userIdComparator = new EntityIdComparator<>();
|
||||
|
||||
private CustomerId customerNUULId = (CustomerId) createEntityId_NULL_UUID(new Customer());
|
||||
|
||||
@Autowired
|
||||
@ -651,6 +655,89 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUsersForAssign() throws Exception {
|
||||
loginTenantAdmin();
|
||||
|
||||
String email = "testEmail1";
|
||||
List<UserId> expectedCustomerUserIds = new ArrayList<>();
|
||||
expectedCustomerUserIds.add(customerUserId);
|
||||
for (int i = 0; i < 45; i++) {
|
||||
User customerUser = createCustomerUser( customerId);
|
||||
customerUser.setEmail(email + StringUtils.randomAlphanumeric((int) (5 + Math.random() * 10)) + "@thingsboard.org");
|
||||
User user = doPost("/api/user", customerUser, User.class);
|
||||
expectedCustomerUserIds.add(user.getId());
|
||||
}
|
||||
List<UserId> expectedTenantUserIds = new ArrayList<>(List.copyOf(expectedCustomerUserIds));
|
||||
expectedTenantUserIds.add(tenantAdminUserId);
|
||||
|
||||
Device device = new Device();
|
||||
device.setName("testDevice");
|
||||
Device savedDevice = doPost("/api/device", device, Device.class);
|
||||
|
||||
Alarm alarm = createTestAlarm(savedDevice);
|
||||
|
||||
List<UserId> loadedTenantUserIds = new ArrayList<>();
|
||||
PageLink pageLink = new PageLink(33, 0);
|
||||
PageData<UserEmailInfo> pageData;
|
||||
do {
|
||||
pageData = doGetTypedWithPageLink("/api/users/assign/" + alarm.getId().getId().toString() + "?",
|
||||
new TypeReference<>() {}, pageLink);
|
||||
loadedTenantUserIds.addAll(pageData.getData().stream().map(UserEmailInfo::getId)
|
||||
.collect(Collectors.toList()));
|
||||
if (pageData.hasNext()) {
|
||||
pageLink = pageLink.nextPageLink();
|
||||
}
|
||||
} while (pageData.hasNext());
|
||||
|
||||
Assert.assertEquals(1, loadedTenantUserIds.size());
|
||||
Assert.assertEquals(tenantAdminUserId, loadedTenantUserIds.get(0));
|
||||
|
||||
doDelete("/api/alarm/" + alarm.getId().getId().toString());
|
||||
|
||||
savedDevice.setCustomerId(customerId);
|
||||
savedDevice = doPost("/api/customer/" + customerId.getId()
|
||||
+ "/device/" + savedDevice.getId().getId(), Device.class);
|
||||
|
||||
alarm = createTestAlarm(savedDevice);
|
||||
|
||||
List<UserId> loadedUserIds = new ArrayList<>();
|
||||
pageLink = new PageLink(16, 0);
|
||||
do {
|
||||
pageData = doGetTypedWithPageLink("/api/users/assign/" + alarm.getId().getId().toString() + "?",
|
||||
new TypeReference<>() {}, pageLink);
|
||||
loadedUserIds.addAll(pageData.getData().stream().map(UserEmailInfo::getId)
|
||||
.collect(Collectors.toList()));
|
||||
if (pageData.hasNext()) {
|
||||
pageLink = pageLink.nextPageLink();
|
||||
}
|
||||
} while (pageData.hasNext());
|
||||
|
||||
expectedTenantUserIds.sort(userIdComparator);
|
||||
loadedUserIds.sort(userIdComparator);
|
||||
|
||||
Assert.assertEquals(expectedTenantUserIds, loadedUserIds);
|
||||
|
||||
loginCustomerUser();
|
||||
|
||||
loadedUserIds = new ArrayList<>();
|
||||
pageLink = new PageLink(16, 0);
|
||||
do {
|
||||
pageData = doGetTypedWithPageLink("/api/users/assign/" + alarm.getId().getId().toString() + "?",
|
||||
new TypeReference<>() {}, pageLink);
|
||||
loadedUserIds.addAll(pageData.getData().stream().map(UserEmailInfo::getId)
|
||||
.collect(Collectors.toList()));
|
||||
if (pageData.hasNext()) {
|
||||
pageLink = pageLink.nextPageLink();
|
||||
}
|
||||
} while (pageData.hasNext());
|
||||
|
||||
expectedCustomerUserIds.sort(userIdComparator);
|
||||
loadedUserIds.sort(userIdComparator);
|
||||
|
||||
Assert.assertEquals(expectedCustomerUserIds, loadedUserIds);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteUserWithDeleteRelationsOk() throws Exception {
|
||||
loginSysAdmin();
|
||||
@ -991,6 +1078,16 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
|
||||
return loadedCustomerUsers;
|
||||
}
|
||||
|
||||
private Alarm createTestAlarm(Device device) {
|
||||
Alarm alarm = new Alarm();
|
||||
alarm.setOriginator(device.getId());
|
||||
alarm.setCustomerId(device.getCustomerId());
|
||||
alarm.setSeverity(AlarmSeverity.MAJOR);
|
||||
alarm.setType("testAlarm");
|
||||
alarm.setStartTs(System.currentTimeMillis());
|
||||
return doPost("/api/alarm", alarm, Alarm.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyDashboardSettings() throws Exception {
|
||||
loginCustomerUser();
|
||||
|
||||
@ -77,6 +77,8 @@ public interface UserService extends EntityDaoService {
|
||||
|
||||
PageData<User> findCustomerUsers(TenantId tenantId, CustomerId customerId, PageLink pageLink);
|
||||
|
||||
PageData<User> findUsersByCustomerIds(TenantId tenantId, List<CustomerId> customerIds, PageLink pageLink);
|
||||
|
||||
void deleteCustomerUsers(TenantId tenantId, CustomerId customerId);
|
||||
|
||||
void setUserCredentialsEnabled(TenantId tenantId, UserId userId, boolean enabled);
|
||||
|
||||
@ -20,6 +20,7 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
import org.thingsboard.server.common.data.id.CustomerId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.id.TenantProfileId;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
@ -102,6 +103,17 @@ public class JpaUserDao extends JpaAbstractSearchTextDao<UserEntity, User> imple
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageData<User> findUsersByCustomerIds(UUID tenantId, List<CustomerId> customerIds, PageLink pageLink) {
|
||||
return DaoUtil.toPageData(
|
||||
userRepository
|
||||
.findTenantAndCustomerUsers(
|
||||
tenantId,
|
||||
DaoUtil.toUUIDs(customerIds),
|
||||
Objects.toString(pageLink.getTextSearch(), ""),
|
||||
DaoUtil.toPageable(pageLink)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageData<User> findAll(PageLink pageLink) {
|
||||
return DaoUtil.toPageData(userRepository.findAll(DaoUtil.toPageable(pageLink)));
|
||||
|
||||
@ -44,6 +44,14 @@ public interface UserRepository extends JpaRepository<UserEntity, UUID> {
|
||||
@Param("authority") Authority authority,
|
||||
Pageable pageable);
|
||||
|
||||
@Query("SELECT u FROM UserEntity u WHERE u.tenantId = :tenantId " +
|
||||
"AND u.customerId IN (:customerIds) " +
|
||||
"AND LOWER(u.searchText) LIKE LOWER(CONCAT('%', :searchText, '%'))")
|
||||
Page<UserEntity> findTenantAndCustomerUsers(@Param("tenantId") UUID tenantId,
|
||||
@Param("customerIds") Collection<UUID> customerIds,
|
||||
@Param("searchText") String searchText,
|
||||
Pageable pageable);
|
||||
|
||||
@Query("SELECT u FROM UserEntity u WHERE u.tenantId = :tenantId " +
|
||||
"AND LOWER(u.searchText) LIKE LOWER(CONCAT('%', :searchText, '%'))")
|
||||
Page<UserEntity> findByTenantId(@Param("tenantId") UUID tenantId,
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
package org.thingsboard.server.dao.user;
|
||||
|
||||
import org.thingsboard.server.common.data.User;
|
||||
import org.thingsboard.server.common.data.id.CustomerId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.id.TenantProfileId;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
@ -82,6 +83,16 @@ public interface UserDao extends Dao<User>, TenantEntityDao {
|
||||
*/
|
||||
PageData<User> findCustomerUsers(UUID tenantId, UUID customerId, PageLink pageLink);
|
||||
|
||||
/**
|
||||
* Find users for alarm assignment by tenantId, customerId and page link.
|
||||
*
|
||||
* @param tenantId the tenantId
|
||||
* @param customerId the customerId
|
||||
* @param pageLink the page link
|
||||
* @return the list of user entities
|
||||
*/
|
||||
PageData<User> findUsersByCustomerIds(UUID tenantId, List<CustomerId> customerIds, PageLink pageLink);
|
||||
|
||||
PageData<User> findAll(PageLink pageLink);
|
||||
|
||||
PageData<User> findAllByAuthority(Authority authority, PageLink pageLink);
|
||||
|
||||
@ -296,6 +296,15 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
|
||||
return userDao.findCustomerUsers(tenantId.getId(), customerId.getId(), pageLink);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageData<User> findUsersByCustomerIds(TenantId tenantId, List<CustomerId> customerIds, PageLink pageLink) {
|
||||
log.trace("Executing findTenantAndCustomerUsers, tenantId [{}], customerIds [{}], pageLink [{}]", tenantId, customerIds, pageLink);
|
||||
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
|
||||
validatePageLink(pageLink);
|
||||
customerIds.forEach(customerId -> {validateId(customerId, "Incorrect customerId " + customerId);});
|
||||
return userDao.findUsersByCustomerIds(tenantId.getId(), customerIds, pageLink);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteCustomerUsers(TenantId tenantId, CustomerId customerId) {
|
||||
log.trace("Executing deleteCustomerUsers, customerId [{}]", customerId);
|
||||
|
||||
@ -65,6 +65,7 @@ import org.thingsboard.server.common.data.TenantProfile;
|
||||
import org.thingsboard.server.common.data.UpdateMessage;
|
||||
import org.thingsboard.server.common.data.UsageInfo;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
import org.thingsboard.server.common.data.UserEmailInfo;
|
||||
import org.thingsboard.server.common.data.alarm.Alarm;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmComment;
|
||||
import org.thingsboard.server.common.data.alarm.AlarmCommentInfo;
|
||||
@ -2628,6 +2629,19 @@ public class RestClient implements Closeable {
|
||||
}, params).getBody();
|
||||
}
|
||||
|
||||
public PageData<UserEmailInfo> getUsersForAssign(AlarmId alarmId, PageLink pageLink) {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("alarmId", alarmId.getId().toString());
|
||||
addPageLinkToParam(params, pageLink);
|
||||
|
||||
return restTemplate.exchange(
|
||||
baseURL + "/users/assign/{alarmId}" + getUrlParams(pageLink),
|
||||
HttpMethod.GET,
|
||||
HttpEntity.EMPTY,
|
||||
new ParameterizedTypeReference<PageData<UserEmailInfo>>() {
|
||||
}, params).getBody();
|
||||
}
|
||||
|
||||
public void setUserCredentialsEnabled(UserId userId, boolean userCredentialsEnabled) {
|
||||
restTemplate.postForLocation(
|
||||
baseURL + "/api/user/{userId}/userCredentialsEnabled?userCredentialsEnabled={userCredentialsEnabled}",
|
||||
|
||||
@ -51,6 +51,12 @@ export class UserService {
|
||||
defaultHttpOptionsFromConfig(config));
|
||||
}
|
||||
|
||||
public getUsersForAssign(alarmId: string, pageLink: PageLink,
|
||||
config?: RequestConfig): Observable<PageData<UserEmailInfo>> {
|
||||
return this.http.get<PageData<UserEmailInfo>>(`/api/users/assign/${alarmId}${pageLink.toQuery()}`,
|
||||
defaultHttpOptionsFromConfig(config));
|
||||
}
|
||||
|
||||
public getUser(userId: string, config?: RequestConfig): Observable<User> {
|
||||
return this.http.get<User>(`/api/user/${userId}`, defaultHttpOptionsFromConfig(config));
|
||||
}
|
||||
|
||||
@ -152,7 +152,7 @@ export class AlarmAssigneePanelComponent implements OnInit, AfterViewInit, OnDe
|
||||
property: 'email',
|
||||
direction: Direction.ASC
|
||||
});
|
||||
return this.userService.findUsersByQuery(pageLink, {ignoreLoading: true})
|
||||
return this.userService.getUsersForAssign(this.alarmId, pageLink, {ignoreLoading: true})
|
||||
.pipe(
|
||||
catchError(() => of(emptyPageData<UserEmailInfo>())),
|
||||
map(pageData => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user