Browse Source
Merge pull request #14235 from dashevchenko/tenantUserDeletion
Added validation that prohibits last tenant admin deletion
pull/14241/head
Viacheslav Klimov
7 months ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with
37 additions and
0 deletions
-
application/src/main/java/org/thingsboard/server/controller/UserController.java
-
application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java
-
common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java
-
dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java
-
dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java
-
dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java
-
dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java
|
|
|
@ -266,6 +266,9 @@ public class UserController extends BaseController { |
|
|
|
if (user.getAuthority() == Authority.SYS_ADMIN && getCurrentUser().getId().equals(userId)) { |
|
|
|
throw new ThingsboardException("Sysadmin is not allowed to delete himself", ThingsboardErrorCode.PERMISSION_DENIED); |
|
|
|
} |
|
|
|
if (user.getAuthority() == Authority.TENANT_ADMIN && userService.countTenantAdmins(user.getTenantId()) == 1) { |
|
|
|
throw new ThingsboardException("At least one tenant administrator must remain!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); |
|
|
|
} |
|
|
|
tbUserService.delete(getTenantId(), getCurrentUser().getCustomerId(), user, getCurrentUser()); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -284,6 +284,26 @@ public class UserControllerTest extends AbstractControllerTest { |
|
|
|
ActionType.ADDED, new DataValidationException(msgError)); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void testShouldNotDeleteLastTenantAdmin() throws Exception { |
|
|
|
loginSysAdmin(); |
|
|
|
|
|
|
|
User tenantAdmin2 = new User(); |
|
|
|
tenantAdmin2.setAuthority(Authority.TENANT_ADMIN); |
|
|
|
tenantAdmin2.setTenantId(tenantId); |
|
|
|
tenantAdmin2.setEmail("tenant2@thingsboard.io"); |
|
|
|
tenantAdmin2 = doPost("/api/user", tenantAdmin2, User.class); |
|
|
|
|
|
|
|
// delete second tenant admin - ok
|
|
|
|
doDelete("/api/user/" + tenantAdmin2.getId().getId().toString()) |
|
|
|
.andExpect(status().isOk()); |
|
|
|
|
|
|
|
// delete last tenant admin - forbidden
|
|
|
|
doDelete("/api/user/" + tenantAdminUser.getId().getId().toString()) |
|
|
|
.andExpect(status().isBadRequest()) |
|
|
|
.andExpect(statusReason(containsString("At least one tenant administrator must remain!"))); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void testSaveUserWithInvalidEmail() throws Exception { |
|
|
|
loginSysAdmin(); |
|
|
|
|
|
|
|
@ -109,4 +109,5 @@ public interface UserService extends EntityDaoService { |
|
|
|
|
|
|
|
void removeMobileSession(TenantId tenantId, String mobileToken); |
|
|
|
|
|
|
|
int countTenantAdmins(TenantId tenantId); |
|
|
|
} |
|
|
|
|
|
|
|
@ -136,6 +136,11 @@ public class JpaUserDao extends JpaAbstractDao<UserEntity, User> implements User |
|
|
|
DaoUtil.toPageable(pageLink))); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public int countTenantAdmins(UUID tenantId) { |
|
|
|
return userRepository.countByTenantIdAndAuthority(tenantId, Authority.TENANT_ADMIN); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public Long countByTenantId(TenantId tenantId) { |
|
|
|
return userRepository.countByTenantId(tenantId.getId()); |
|
|
|
|
|
|
|
@ -78,4 +78,6 @@ public interface UserRepository extends JpaRepository<UserEntity, UUID> { |
|
|
|
"u.customerId, u.version, u.firstName, u.lastName, u.email, u.phone, u.additionalInfo) " + |
|
|
|
"FROM UserEntity u WHERE u.id > :id ORDER BY u.id") |
|
|
|
List<UserFields> findNextBatch(@Param("id") UUID id, Limit limit); |
|
|
|
|
|
|
|
int countByTenantIdAndAuthority(UUID tenantId, Authority authority); |
|
|
|
} |
|
|
|
|
|
|
|
@ -101,4 +101,5 @@ public interface UserDao extends Dao<User>, TenantEntityDao<User> { |
|
|
|
|
|
|
|
PageData<User> findByAuthorityAndTenantProfilesIds(Authority authority, List<TenantProfileId> tenantProfilesIds, PageLink pageLink); |
|
|
|
|
|
|
|
int countTenantAdmins(UUID tenantId); |
|
|
|
} |
|
|
|
|
|
|
|
@ -485,6 +485,11 @@ public class UserServiceImpl extends AbstractCachedEntityService<UserCacheKey, U |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public int countTenantAdmins(TenantId tenantId) { |
|
|
|
return userDao.countTenantAdmins(tenantId.getId()); |
|
|
|
} |
|
|
|
|
|
|
|
private Optional<UserMobileSessionInfo> findMobileSessionInfo(TenantId tenantId, UserId userId) { |
|
|
|
return Optional.ofNullable(userSettingsService.findUserSettings(tenantId, userId, UserSettingsType.MOBILE)) |
|
|
|
.map(UserSettings::getSettings).map(settings -> JacksonUtil.treeToValue(settings, UserMobileSessionInfo.class)); |
|
|
|
|