Commit 223a4d816aa1c198dbc3b8e252700e183ee709a1

Authored by YunaiV
1 parent 459986bf

fix(system): 禁止禁用或删除超级管理员和租户管理员账号

yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
... ... @@ -46,6 +46,8 @@ public interface ErrorCodeConstants {
46 46 ErrorCode USER_IMPORT_INIT_PASSWORD = new ErrorCode(1_002_003_009, "初始密码不能为空");
47 47 ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1_002_003_010, "该手机号尚未注册");
48 48 ErrorCode USER_REGISTER_DISABLED = new ErrorCode(1_002_003_011, "注册功能已关闭");
  49 + ErrorCode USER_ADMIN_NOT_ALLOW_DISABLE = new ErrorCode(1_002_003_012, "超级管理员或租户管理员账号不能禁用");
  50 + ErrorCode USER_ADMIN_NOT_ALLOW_DELETE = new ErrorCode(1_002_003_013, "超级管理员或租户管理员账号不能删除");
49 51  
50 52 // ========== 部门模块 1-002-004-000 ==========
51 53 ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1_002_004_000, "已经存在该名字的部门");
... ...
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
... ... @@ -6,10 +6,12 @@ import cn.hutool.core.util.ObjUtil;
6 6 import cn.hutool.core.util.StrUtil;
7 7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
8 8 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
  9 +import cn.iocoder.yudao.framework.common.exception.ErrorCode;
9 10 import cn.iocoder.yudao.framework.common.exception.ServiceException;
10 11 import cn.iocoder.yudao.framework.common.pojo.PageResult;
11 12 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
12 13 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  14 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
13 15 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
14 16 import cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils;
15 17 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
22 24 import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSaveReqVO;
23 25 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
24 26 import cn.iocoder.yudao.module.system.dal.dataobject.dept.UserPostDO;
  27 +import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
25 28 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
26 29 import cn.iocoder.yudao.module.system.dal.mysql.dept.UserPostMapper;
27 30 import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper;
  31 +import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
28 32 import cn.iocoder.yudao.module.system.mq.producer.user.AdminUserProducer;
29 33 import cn.iocoder.yudao.module.system.service.dept.DeptService;
30 34 import cn.iocoder.yudao.module.system.service.dept.PostService;
31 35 import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService;
32 36 import cn.iocoder.yudao.module.system.service.permission.PermissionService;
  37 +import cn.iocoder.yudao.module.system.service.permission.RoleService;
33 38 import cn.iocoder.yudao.module.system.service.tenant.TenantService;
34 39 import com.google.common.annotations.VisibleForTesting;
35 40 import com.mzt.logapi.context.LogRecordContext;
... ... @@ -75,6 +80,8 @@ public class AdminUserServiceImpl implements AdminUserService {
75 80 @Resource
76 81 private PermissionService permissionService;
77 82 @Resource
  83 + private RoleService roleService;
  84 + @Resource
78 85 private PasswordEncoder passwordEncoder;
79 86 @Resource
80 87 @Lazy // 延迟,避免循环依赖报错
... ... @@ -250,15 +257,20 @@ public class AdminUserServiceImpl implements AdminUserService {
250 257  
251 258 @Override
252 259 public void updateUserStatus(Long id, Integer status) {
253   - // 校验用户存在
  260 + // 1.1 校验用户存在
254 261 validateUserExists(id);
255   - // 更新状态
  262 + // 1.2 管理员不能禁用
  263 + if (CommonStatusEnum.isDisable(status)) {
  264 + validateUserNotAdmin(id, USER_ADMIN_NOT_ALLOW_DISABLE);
  265 + }
  266 +
  267 + // 2. 更新状态
256 268 AdminUserDO updateObj = new AdminUserDO();
257 269 updateObj.setId(id);
258 270 updateObj.setStatus(status);
259 271 userMapper.updateById(updateObj);
260 272  
261   - // 如果是禁用用户,则删除其 Token 信息
  273 + // 3. 如果是禁用用户,则删除其 Token 信息
262 274 if (CommonStatusEnum.isDisable(status)) {
263 275 oauth2TokenService.removeAccessToken(id, UserTypeEnum.ADMIN.getValue());
264 276 }
... ... @@ -269,8 +281,10 @@ public class AdminUserServiceImpl implements AdminUserService {
269 281 @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_DELETE_SUB_TYPE, bizNo = "{{#id}}",
270 282 success = SYSTEM_USER_DELETE_SUCCESS)
271 283 public void deleteUser(Long id) {
272   - // 1. 校验用户存在
  284 + // 1.1 校验用户存在
273 285 AdminUserDO user = validateUserExists(id);
  286 + // 1.2 管理员不能删除
  287 + validateUserNotAdmin(id, USER_ADMIN_NOT_ALLOW_DELETE);
274 288  
275 289 // 2.1 删除用户
276 290 userMapper.deleteById(id);
... ... @@ -286,16 +300,32 @@ public class AdminUserServiceImpl implements AdminUserService {
286 300 @Override
287 301 @Transactional(rollbackFor = Exception.class)
288 302 public void deleteUserList(List<Long> ids) {
289   - // 1. 批量删除用户
  303 + // 1. 校验不能删除管理员
  304 + ids.forEach(id -> validateUserNotAdmin(id, USER_ADMIN_NOT_ALLOW_DELETE));
  305 +
  306 + // 2. 批量删除用户
290 307 userMapper.deleteByIds(ids);
291 308  
292   - // 2. 批量删除用户关联数据
  309 + // 3. 批量删除用户关联数据
293 310 ids.forEach(id -> {
294 311 permissionService.processUserDeleted(id);
295 312 userPostMapper.deleteByUserId(id);
296 313 });
297 314 }
298 315  
  316 + private void validateUserNotAdmin(Long id, ErrorCode errorCode) {
  317 + Set<Long> roleIds = permissionService.getUserRoleIdListByUserId(id);
  318 + if (CollUtil.isEmpty(roleIds)) {
  319 + return;
  320 + }
  321 + for (RoleDO role : roleService.getRoleList(roleIds)) {
  322 + if (ObjectUtils.equalsAny(role.getCode(),
  323 + RoleCodeEnum.SUPER_ADMIN.getCode(), RoleCodeEnum.TENANT_ADMIN.getCode())) {
  324 + throw exception(errorCode);
  325 + }
  326 + }
  327 + }
  328 +
299 329 @Override
300 330 public AdminUserDO getUserByUsername(String username) {
301 331 return userMapper.selectByUsername(username);
... ... @@ -562,6 +592,16 @@ public class AdminUserServiceImpl implements AdminUserService {
562 592 respVO.getFailureUsernames().put(importUser.getUsername(), USER_USERNAME_EXISTS.getMsg());
563 593 return;
564 594 }
  595 + // 2.2.3 校验管理员不能禁用
  596 + if (CommonStatusEnum.isDisable(importUser.getStatus())) {
  597 + try {
  598 + validateUserNotAdmin(existUser.getId(), USER_ADMIN_NOT_ALLOW_DISABLE);
  599 + } catch (ServiceException ex) {
  600 + respVO.getFailureUsernames().put(importUser.getUsername(), ex.getMessage());
  601 + return;
  602 + }
  603 + }
  604 + // 2.2.4 更新用户
565 605 AdminUserDO updateUser = BeanUtils.toBean(importUser, AdminUserDO.class);
566 606 updateUser.setId(existUser.getId());
567 607 userMapper.updateById(updateUser);
... ...
yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java
... ... @@ -19,16 +19,19 @@ import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSaveReqV
19 19 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
20 20 import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO;
21 21 import cn.iocoder.yudao.module.system.dal.dataobject.dept.UserPostDO;
  22 +import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
22 23 import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
23 24 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
24 25 import cn.iocoder.yudao.module.system.dal.mysql.dept.UserPostMapper;
25 26 import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper;
26 27 import cn.iocoder.yudao.module.system.enums.common.SexEnum;
  28 +import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
27 29 import cn.iocoder.yudao.module.system.mq.producer.user.AdminUserProducer;
28 30 import cn.iocoder.yudao.module.system.service.dept.DeptService;
29 31 import cn.iocoder.yudao.module.system.service.dept.PostService;
30 32 import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService;
31 33 import cn.iocoder.yudao.module.system.service.permission.PermissionService;
  34 +import cn.iocoder.yudao.module.system.service.permission.RoleService;
32 35 import cn.iocoder.yudao.module.system.service.tenant.TenantService;
33 36 import jakarta.annotation.Resource;
34 37 import org.junit.jupiter.api.BeforeEach;
... ... @@ -78,6 +81,8 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
78 81 @MockitoBean
79 82 private PermissionService permissionService;
80 83 @MockitoBean
  84 + private RoleService roleService;
  85 + @MockitoBean
81 86 private PasswordEncoder passwordEncoder;
82 87 @MockitoBean
83 88 private TenantService tenantService;
... ... @@ -290,6 +295,35 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
290 295 }
291 296  
292 297 @Test
  298 + public void testUpdateUserStatus_adminCannotDisable() {
  299 + for (RoleCodeEnum roleCode : new RoleCodeEnum[]{RoleCodeEnum.SUPER_ADMIN, RoleCodeEnum.TENANT_ADMIN}) {
  300 + // mock 数据
  301 + AdminUserDO dbUser = randomAdminUserDO(o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
  302 + userMapper.insert(dbUser);
  303 + mockUserRole(dbUser.getId(), roleCode);
  304 +
  305 + // 调用,并断言异常
  306 + assertServiceException(() -> userService.updateUserStatus(dbUser.getId(), CommonStatusEnum.DISABLE.getStatus()),
  307 + USER_ADMIN_NOT_ALLOW_DISABLE);
  308 + assertEquals(CommonStatusEnum.ENABLE.getStatus(), userMapper.selectById(dbUser.getId()).getStatus());
  309 + }
  310 + verify(oauth2TokenService, never()).removeAccessToken(anyLong(), anyInt());
  311 + }
  312 +
  313 + @Test
  314 + public void testUpdateUserStatus_adminCanEnable() {
  315 + // mock 数据
  316 + AdminUserDO dbUser = randomAdminUserDO(o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
  317 + userMapper.insert(dbUser);
  318 + mockUserRole(dbUser.getId(), RoleCodeEnum.TENANT_ADMIN);
  319 +
  320 + // 调用
  321 + userService.updateUserStatus(dbUser.getId(), CommonStatusEnum.ENABLE.getStatus());
  322 + // 断言
  323 + assertEquals(CommonStatusEnum.ENABLE.getStatus(), userMapper.selectById(dbUser.getId()).getStatus());
  324 + }
  325 +
  326 + @Test
293 327 public void testDeleteUser_success(){
294 328 // mock 数据
295 329 AdminUserDO dbUser = randomAdminUserDO();
... ... @@ -306,6 +340,43 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
306 340 }
307 341  
308 342 @Test
  343 + public void testDeleteUser_adminCannotDelete() {
  344 + for (RoleCodeEnum roleCode : new RoleCodeEnum[]{RoleCodeEnum.SUPER_ADMIN, RoleCodeEnum.TENANT_ADMIN}) {
  345 + // mock 数据
  346 + AdminUserDO dbUser = randomAdminUserDO();
  347 + userMapper.insert(dbUser);
  348 + mockUserRole(dbUser.getId(), roleCode);
  349 +
  350 + // 调用,并断言异常
  351 + assertServiceException(() -> userService.deleteUser(dbUser.getId()), USER_ADMIN_NOT_ALLOW_DELETE);
  352 + assertNotNull(userMapper.selectById(dbUser.getId()));
  353 + }
  354 + }
  355 +
  356 + @Test
  357 + public void testDeleteUserList_adminCannotDelete() {
  358 + // mock 数据
  359 + AdminUserDO ordinaryUser = randomAdminUserDO();
  360 + userMapper.insert(ordinaryUser);
  361 + AdminUserDO adminUser = randomAdminUserDO();
  362 + userMapper.insert(adminUser);
  363 + mockUserRole(adminUser.getId(), RoleCodeEnum.TENANT_ADMIN);
  364 +
  365 + // 调用,并断言整批未删除
  366 + assertServiceException(() -> userService.deleteUserList(List.of(ordinaryUser.getId(), adminUser.getId())),
  367 + USER_ADMIN_NOT_ALLOW_DELETE);
  368 + assertNotNull(userMapper.selectById(ordinaryUser.getId()));
  369 + assertNotNull(userMapper.selectById(adminUser.getId()));
  370 + }
  371 +
  372 + private void mockUserRole(Long userId, RoleCodeEnum roleCode) {
  373 + Long roleId = 100L;
  374 + when(permissionService.getUserRoleIdListByUserId(userId)).thenReturn(asSet(roleId));
  375 + when(roleService.getRoleList(asSet(roleId))).thenReturn(singletonList(
  376 + randomPojo(RoleDO.class, role -> role.setCode(roleCode.getCode()))));
  377 + }
  378 +
  379 + @Test
309 380 public void testGetUserByUsername() {
310 381 // mock 数据
311 382 AdminUserDO dbUser = randomAdminUserDO();
... ... @@ -533,6 +604,29 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
533 604 }
534 605  
535 606 @Test
  607 + public void testImportUserList_adminCannotDisable() {
  608 + // mock 数据
  609 + AdminUserDO dbUser = randomAdminUserDO(o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
  610 + userMapper.insert(dbUser);
  611 + mockUserRole(dbUser.getId(), RoleCodeEnum.TENANT_ADMIN);
  612 + // 准备参数
  613 + UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
  614 + o.setUsername(dbUser.getUsername());
  615 + o.setStatus(CommonStatusEnum.DISABLE.getStatus());
  616 + o.setSex(randomEle(SexEnum.values()).getSex());
  617 + o.setEmail(randomEmail());
  618 + o.setMobile(randomMobile());
  619 + });
  620 +
  621 + // 调用
  622 + UserImportRespVO respVO = userService.importUserList(singletonList(importUser), true);
  623 + // 断言
  624 + assertEquals(USER_ADMIN_NOT_ALLOW_DISABLE.getMsg(), respVO.getFailureUsernames().get(importUser.getUsername()));
  625 + assertTrue(respVO.getUpdateUsernames().isEmpty());
  626 + assertEquals(CommonStatusEnum.ENABLE.getStatus(), userMapper.selectById(dbUser.getId()).getStatus());
  627 + }
  628 +
  629 + @Test
536 630 public void testValidateUserExists_notExists() {
537 631 assertServiceException(() -> userService.validateUserExists(randomLongId()), USER_NOT_EXISTS);
538 632 }
... ...