mirror of
https://gitee.com/zhijiantianya/ruoyi-vue-pro.git
synced 2026-10-06 07:22:59 +08:00
fix(system): 禁止禁用或删除超级管理员和租户管理员账号
This commit is contained in:
+2
@@ -46,6 +46,8 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode USER_IMPORT_INIT_PASSWORD = new ErrorCode(1_002_003_009, "初始密码不能为空");
|
||||
ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1_002_003_010, "该手机号尚未注册");
|
||||
ErrorCode USER_REGISTER_DISABLED = new ErrorCode(1_002_003_011, "注册功能已关闭");
|
||||
ErrorCode USER_ADMIN_NOT_ALLOW_DISABLE = new ErrorCode(1_002_003_012, "超级管理员或租户管理员账号不能禁用");
|
||||
ErrorCode USER_ADMIN_NOT_ALLOW_DELETE = new ErrorCode(1_002_003_013, "超级管理员或租户管理员账号不能删除");
|
||||
|
||||
// ========== 部门模块 1-002-004-000 ==========
|
||||
ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1_002_004_000, "已经存在该名字的部门");
|
||||
|
||||
+46
-6
@@ -6,10 +6,12 @@ import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
|
||||
import cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils;
|
||||
import cn.iocoder.yudao.module.infra.api.config.ConfigApi;
|
||||
@@ -22,14 +24,17 @@ import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserPageReqV
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSaveReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.dept.UserPostDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.dept.UserPostMapper;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper;
|
||||
import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
|
||||
import cn.iocoder.yudao.module.system.mq.producer.user.AdminUserProducer;
|
||||
import cn.iocoder.yudao.module.system.service.dept.DeptService;
|
||||
import cn.iocoder.yudao.module.system.service.dept.PostService;
|
||||
import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService;
|
||||
import cn.iocoder.yudao.module.system.service.permission.PermissionService;
|
||||
import cn.iocoder.yudao.module.system.service.permission.RoleService;
|
||||
import cn.iocoder.yudao.module.system.service.tenant.TenantService;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.mzt.logapi.context.LogRecordContext;
|
||||
@@ -75,6 +80,8 @@ public class AdminUserServiceImpl implements AdminUserService {
|
||||
@Resource
|
||||
private PermissionService permissionService;
|
||||
@Resource
|
||||
private RoleService roleService;
|
||||
@Resource
|
||||
private PasswordEncoder passwordEncoder;
|
||||
@Resource
|
||||
@Lazy // 延迟,避免循环依赖报错
|
||||
@@ -250,15 +257,20 @@ public class AdminUserServiceImpl implements AdminUserService {
|
||||
|
||||
@Override
|
||||
public void updateUserStatus(Long id, Integer status) {
|
||||
// 校验用户存在
|
||||
// 1.1 校验用户存在
|
||||
validateUserExists(id);
|
||||
// 更新状态
|
||||
// 1.2 管理员不能禁用
|
||||
if (CommonStatusEnum.isDisable(status)) {
|
||||
validateUserNotAdmin(id, USER_ADMIN_NOT_ALLOW_DISABLE);
|
||||
}
|
||||
|
||||
// 2. 更新状态
|
||||
AdminUserDO updateObj = new AdminUserDO();
|
||||
updateObj.setId(id);
|
||||
updateObj.setStatus(status);
|
||||
userMapper.updateById(updateObj);
|
||||
|
||||
// 如果是禁用用户,则删除其 Token 信息
|
||||
// 3. 如果是禁用用户,则删除其 Token 信息
|
||||
if (CommonStatusEnum.isDisable(status)) {
|
||||
oauth2TokenService.removeAccessToken(id, UserTypeEnum.ADMIN.getValue());
|
||||
}
|
||||
@@ -269,8 +281,10 @@ public class AdminUserServiceImpl implements AdminUserService {
|
||||
@LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_DELETE_SUB_TYPE, bizNo = "{{#id}}",
|
||||
success = SYSTEM_USER_DELETE_SUCCESS)
|
||||
public void deleteUser(Long id) {
|
||||
// 1. 校验用户存在
|
||||
// 1.1 校验用户存在
|
||||
AdminUserDO user = validateUserExists(id);
|
||||
// 1.2 管理员不能删除
|
||||
validateUserNotAdmin(id, USER_ADMIN_NOT_ALLOW_DELETE);
|
||||
|
||||
// 2.1 删除用户
|
||||
userMapper.deleteById(id);
|
||||
@@ -286,16 +300,32 @@ public class AdminUserServiceImpl implements AdminUserService {
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteUserList(List<Long> ids) {
|
||||
// 1. 批量删除用户
|
||||
// 1. 校验不能删除管理员
|
||||
ids.forEach(id -> validateUserNotAdmin(id, USER_ADMIN_NOT_ALLOW_DELETE));
|
||||
|
||||
// 2. 批量删除用户
|
||||
userMapper.deleteByIds(ids);
|
||||
|
||||
// 2. 批量删除用户关联数据
|
||||
// 3. 批量删除用户关联数据
|
||||
ids.forEach(id -> {
|
||||
permissionService.processUserDeleted(id);
|
||||
userPostMapper.deleteByUserId(id);
|
||||
});
|
||||
}
|
||||
|
||||
private void validateUserNotAdmin(Long id, ErrorCode errorCode) {
|
||||
Set<Long> roleIds = permissionService.getUserRoleIdListByUserId(id);
|
||||
if (CollUtil.isEmpty(roleIds)) {
|
||||
return;
|
||||
}
|
||||
for (RoleDO role : roleService.getRoleList(roleIds)) {
|
||||
if (ObjectUtils.equalsAny(role.getCode(),
|
||||
RoleCodeEnum.SUPER_ADMIN.getCode(), RoleCodeEnum.TENANT_ADMIN.getCode())) {
|
||||
throw exception(errorCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminUserDO getUserByUsername(String username) {
|
||||
return userMapper.selectByUsername(username);
|
||||
@@ -562,6 +592,16 @@ public class AdminUserServiceImpl implements AdminUserService {
|
||||
respVO.getFailureUsernames().put(importUser.getUsername(), USER_USERNAME_EXISTS.getMsg());
|
||||
return;
|
||||
}
|
||||
// 2.2.3 校验管理员不能禁用
|
||||
if (CommonStatusEnum.isDisable(importUser.getStatus())) {
|
||||
try {
|
||||
validateUserNotAdmin(existUser.getId(), USER_ADMIN_NOT_ALLOW_DISABLE);
|
||||
} catch (ServiceException ex) {
|
||||
respVO.getFailureUsernames().put(importUser.getUsername(), ex.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 2.2.4 更新用户
|
||||
AdminUserDO updateUser = BeanUtils.toBean(importUser, AdminUserDO.class);
|
||||
updateUser.setId(existUser.getId());
|
||||
userMapper.updateById(updateUser);
|
||||
|
||||
+94
@@ -19,16 +19,19 @@ import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSaveReqV
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.dept.UserPostDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.dept.UserPostMapper;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper;
|
||||
import cn.iocoder.yudao.module.system.enums.common.SexEnum;
|
||||
import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
|
||||
import cn.iocoder.yudao.module.system.mq.producer.user.AdminUserProducer;
|
||||
import cn.iocoder.yudao.module.system.service.dept.DeptService;
|
||||
import cn.iocoder.yudao.module.system.service.dept.PostService;
|
||||
import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService;
|
||||
import cn.iocoder.yudao.module.system.service.permission.PermissionService;
|
||||
import cn.iocoder.yudao.module.system.service.permission.RoleService;
|
||||
import cn.iocoder.yudao.module.system.service.tenant.TenantService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -78,6 +81,8 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
|
||||
@MockitoBean
|
||||
private PermissionService permissionService;
|
||||
@MockitoBean
|
||||
private RoleService roleService;
|
||||
@MockitoBean
|
||||
private PasswordEncoder passwordEncoder;
|
||||
@MockitoBean
|
||||
private TenantService tenantService;
|
||||
@@ -289,6 +294,35 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
|
||||
verify(oauth2TokenService).removeAccessToken(userId, UserTypeEnum.ADMIN.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateUserStatus_adminCannotDisable() {
|
||||
for (RoleCodeEnum roleCode : new RoleCodeEnum[]{RoleCodeEnum.SUPER_ADMIN, RoleCodeEnum.TENANT_ADMIN}) {
|
||||
// mock 数据
|
||||
AdminUserDO dbUser = randomAdminUserDO(o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||
userMapper.insert(dbUser);
|
||||
mockUserRole(dbUser.getId(), roleCode);
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> userService.updateUserStatus(dbUser.getId(), CommonStatusEnum.DISABLE.getStatus()),
|
||||
USER_ADMIN_NOT_ALLOW_DISABLE);
|
||||
assertEquals(CommonStatusEnum.ENABLE.getStatus(), userMapper.selectById(dbUser.getId()).getStatus());
|
||||
}
|
||||
verify(oauth2TokenService, never()).removeAccessToken(anyLong(), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateUserStatus_adminCanEnable() {
|
||||
// mock 数据
|
||||
AdminUserDO dbUser = randomAdminUserDO(o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
|
||||
userMapper.insert(dbUser);
|
||||
mockUserRole(dbUser.getId(), RoleCodeEnum.TENANT_ADMIN);
|
||||
|
||||
// 调用
|
||||
userService.updateUserStatus(dbUser.getId(), CommonStatusEnum.ENABLE.getStatus());
|
||||
// 断言
|
||||
assertEquals(CommonStatusEnum.ENABLE.getStatus(), userMapper.selectById(dbUser.getId()).getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteUser_success(){
|
||||
// mock 数据
|
||||
@@ -305,6 +339,43 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
|
||||
verify(permissionService, times(1)).processUserDeleted(eq(userId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteUser_adminCannotDelete() {
|
||||
for (RoleCodeEnum roleCode : new RoleCodeEnum[]{RoleCodeEnum.SUPER_ADMIN, RoleCodeEnum.TENANT_ADMIN}) {
|
||||
// mock 数据
|
||||
AdminUserDO dbUser = randomAdminUserDO();
|
||||
userMapper.insert(dbUser);
|
||||
mockUserRole(dbUser.getId(), roleCode);
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> userService.deleteUser(dbUser.getId()), USER_ADMIN_NOT_ALLOW_DELETE);
|
||||
assertNotNull(userMapper.selectById(dbUser.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteUserList_adminCannotDelete() {
|
||||
// mock 数据
|
||||
AdminUserDO ordinaryUser = randomAdminUserDO();
|
||||
userMapper.insert(ordinaryUser);
|
||||
AdminUserDO adminUser = randomAdminUserDO();
|
||||
userMapper.insert(adminUser);
|
||||
mockUserRole(adminUser.getId(), RoleCodeEnum.TENANT_ADMIN);
|
||||
|
||||
// 调用,并断言整批未删除
|
||||
assertServiceException(() -> userService.deleteUserList(List.of(ordinaryUser.getId(), adminUser.getId())),
|
||||
USER_ADMIN_NOT_ALLOW_DELETE);
|
||||
assertNotNull(userMapper.selectById(ordinaryUser.getId()));
|
||||
assertNotNull(userMapper.selectById(adminUser.getId()));
|
||||
}
|
||||
|
||||
private void mockUserRole(Long userId, RoleCodeEnum roleCode) {
|
||||
Long roleId = 100L;
|
||||
when(permissionService.getUserRoleIdListByUserId(userId)).thenReturn(asSet(roleId));
|
||||
when(roleService.getRoleList(asSet(roleId))).thenReturn(singletonList(
|
||||
randomPojo(RoleDO.class, role -> role.setCode(roleCode.getCode()))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUserByUsername() {
|
||||
// mock 数据
|
||||
@@ -532,6 +603,29 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
|
||||
assertEquals(0, respVO.getFailureUsernames().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportUserList_adminCannotDisable() {
|
||||
// mock 数据
|
||||
AdminUserDO dbUser = randomAdminUserDO(o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||
userMapper.insert(dbUser);
|
||||
mockUserRole(dbUser.getId(), RoleCodeEnum.TENANT_ADMIN);
|
||||
// 准备参数
|
||||
UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
|
||||
o.setUsername(dbUser.getUsername());
|
||||
o.setStatus(CommonStatusEnum.DISABLE.getStatus());
|
||||
o.setSex(randomEle(SexEnum.values()).getSex());
|
||||
o.setEmail(randomEmail());
|
||||
o.setMobile(randomMobile());
|
||||
});
|
||||
|
||||
// 调用
|
||||
UserImportRespVO respVO = userService.importUserList(singletonList(importUser), true);
|
||||
// 断言
|
||||
assertEquals(USER_ADMIN_NOT_ALLOW_DISABLE.getMsg(), respVO.getFailureUsernames().get(importUser.getUsername()));
|
||||
assertTrue(respVO.getUpdateUsernames().isEmpty());
|
||||
assertEquals(CommonStatusEnum.ENABLE.getStatus(), userMapper.selectById(dbUser.getId()).getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateUserExists_notExists() {
|
||||
assertServiceException(() -> userService.validateUserExists(randomLongId()), USER_NOT_EXISTS);
|
||||
|
||||
Reference in New Issue
Block a user