修复系统管理后台权限配置校验
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
package cn.nianxx.thhotel.platform.access.service.impl;
|
package cn.nianxx.thhotel.platform.access.service.impl;
|
||||||
|
|
||||||
|
import cn.nianxx.thhotel.platform.access.common.enums.PlatformPermissionStatus;
|
||||||
import cn.nianxx.thhotel.platform.access.common.enums.PlatformRoleStatus;
|
import cn.nianxx.thhotel.platform.access.common.enums.PlatformRoleStatus;
|
||||||
import cn.nianxx.thhotel.platform.access.common.request.AdminRoleCreateRequest;
|
import cn.nianxx.thhotel.platform.access.common.request.AdminRoleCreateRequest;
|
||||||
import cn.nianxx.thhotel.platform.access.common.request.AdminRolePermissionAssignmentRequest;
|
import cn.nianxx.thhotel.platform.access.common.request.AdminRolePermissionAssignmentRequest;
|
||||||
@@ -315,12 +316,13 @@ public class AdminAccessManagementServiceImpl implements AdminAccessManagementSe
|
|||||||
if (permissionIds == null || permissionIds.isEmpty()) {
|
if (permissionIds == null || permissionIds.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Set<Long> existingPermissionIds = accessRepository.listAllPermissions().stream()
|
Set<Long> activePermissionIds = accessRepository.listAllPermissions().stream()
|
||||||
|
.filter(permission -> PlatformPermissionStatus.ACTIVE.name().equals(permission.getPermissionStatus()))
|
||||||
.map(PlatformPermissionEntity::getId)
|
.map(PlatformPermissionEntity::getId)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
if (!existingPermissionIds.containsAll(permissionIds)) {
|
if (!activePermissionIds.containsAll(permissionIds)) {
|
||||||
throw invalidRequest("存在不存在的权限 ID。");
|
throw invalidRequest("存在不存在或未启用的权限 ID。");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package cn.nianxx.thhotel.platform.identity.service.impl;
|
package cn.nianxx.thhotel.platform.identity.service.impl;
|
||||||
|
|
||||||
|
import cn.nianxx.thhotel.platform.access.common.enums.PlatformRoleStatus;
|
||||||
import cn.nianxx.thhotel.platform.access.domain.PlatformRoleEntity;
|
import cn.nianxx.thhotel.platform.access.domain.PlatformRoleEntity;
|
||||||
import cn.nianxx.thhotel.platform.access.domain.PlatformUserRoleEntity;
|
import cn.nianxx.thhotel.platform.access.domain.PlatformUserRoleEntity;
|
||||||
import cn.nianxx.thhotel.platform.access.repository.PlatformAccessRepository;
|
import cn.nianxx.thhotel.platform.access.repository.PlatformAccessRepository;
|
||||||
@@ -479,11 +480,12 @@ public class AdminUserManagementServiceImpl implements AdminUserManagementServic
|
|||||||
if (roleIds == null || roleIds.isEmpty()) {
|
if (roleIds == null || roleIds.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Set<Long> existingRoleIds = accessRepository.listRolesByIds(roleIds).stream()
|
Set<Long> activeRoleIds = accessRepository.listRolesByIds(roleIds).stream()
|
||||||
|
.filter(role -> PlatformRoleStatus.ACTIVE.name().equals(role.getRoleStatus()))
|
||||||
.map(PlatformRoleEntity::getId)
|
.map(PlatformRoleEntity::getId)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
if (!existingRoleIds.containsAll(roleIds)) {
|
if (!activeRoleIds.containsAll(roleIds)) {
|
||||||
throw invalidRequest("存在不存在的角色 ID。");
|
throw invalidRequest("存在不存在或未启用的角色 ID。");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package cn.nianxx.thhotel.platform.navigation.service.impl;
|
package cn.nianxx.thhotel.platform.navigation.service.impl;
|
||||||
|
|
||||||
|
import cn.nianxx.thhotel.platform.access.common.enums.PlatformPermissionStatus;
|
||||||
import cn.nianxx.thhotel.platform.access.repository.PlatformAccessRepository;
|
import cn.nianxx.thhotel.platform.access.repository.PlatformAccessRepository;
|
||||||
import cn.nianxx.thhotel.platform.audit.common.dto.PlatformAdminAuditLogDraft;
|
import cn.nianxx.thhotel.platform.audit.common.dto.PlatformAdminAuditLogDraft;
|
||||||
import cn.nianxx.thhotel.platform.audit.service.PlatformAdminAuditLogService;
|
import cn.nianxx.thhotel.platform.audit.service.PlatformAdminAuditLogService;
|
||||||
@@ -44,7 +45,8 @@ public class AdminMenuManagementServiceImpl implements AdminMenuManagementServic
|
|||||||
"/system/users",
|
"/system/users",
|
||||||
"/system/roles",
|
"/system/roles",
|
||||||
"/system/menus",
|
"/system/menus",
|
||||||
"/system/hotels");
|
"/system/hotels",
|
||||||
|
"/system/audits");
|
||||||
|
|
||||||
private final PlatformNavigationRepository navigationRepository;
|
private final PlatformNavigationRepository navigationRepository;
|
||||||
private final PlatformAccessRepository accessRepository;
|
private final PlatformAccessRepository accessRepository;
|
||||||
@@ -231,8 +233,10 @@ public class AdminMenuManagementServiceImpl implements AdminMenuManagementServic
|
|||||||
menu.setIconKey(trimToNull(iconKey));
|
menu.setIconKey(trimToNull(iconKey));
|
||||||
String normalizedPermissionCode = trimToNull(permissionCode);
|
String normalizedPermissionCode = trimToNull(permissionCode);
|
||||||
if (normalizedPermissionCode != null
|
if (normalizedPermissionCode != null
|
||||||
&& accessRepository.findPermissionByCode(normalizedPermissionCode).isEmpty()) {
|
&& accessRepository.findPermissionByCode(normalizedPermissionCode)
|
||||||
throw invalidRequest("菜单绑定的权限码不存在。");
|
.filter(permission -> PlatformPermissionStatus.ACTIVE.name().equals(permission.getPermissionStatus()))
|
||||||
|
.isEmpty()) {
|
||||||
|
throw invalidRequest("菜单绑定的权限码不存在或未启用。");
|
||||||
}
|
}
|
||||||
menu.setPermissionCode(normalizedPermissionCode);
|
menu.setPermissionCode(normalizedPermissionCode);
|
||||||
menu.setSortOrder(sortOrder == null ? 0 : sortOrder);
|
menu.setSortOrder(sortOrder == null ? 0 : sortOrder);
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
import cn.nianxx.thhotel.ThHotelApplication;
|
import cn.nianxx.thhotel.ThHotelApplication;
|
||||||
|
import cn.nianxx.thhotel.platform.access.common.enums.PlatformPermissionStatus;
|
||||||
import cn.nianxx.thhotel.platform.access.common.enums.PlatformRoleCode;
|
import cn.nianxx.thhotel.platform.access.common.enums.PlatformRoleCode;
|
||||||
|
import cn.nianxx.thhotel.platform.access.common.enums.PlatformRoleStatus;
|
||||||
import cn.nianxx.thhotel.platform.access.domain.PlatformPermissionEntity;
|
import cn.nianxx.thhotel.platform.access.domain.PlatformPermissionEntity;
|
||||||
import cn.nianxx.thhotel.platform.access.domain.PlatformRoleEntity;
|
import cn.nianxx.thhotel.platform.access.domain.PlatformRoleEntity;
|
||||||
import cn.nianxx.thhotel.platform.access.repository.PlatformAccessRepository;
|
import cn.nianxx.thhotel.platform.access.repository.PlatformAccessRepository;
|
||||||
@@ -264,6 +266,58 @@ class AdminReadonlyControllerTest {
|
|||||||
.andExpect(status().isUnauthorized());
|
.andExpect(status().isUnauthorized());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRejectDisabledRoleAssignmentToUser() throws Exception {
|
||||||
|
String token = tokenFrom(login("m006-admin", "Admin@123456"));
|
||||||
|
PlatformRoleEntity viewerRole = accessRepository.findRoleByCode(PlatformRoleCode.RESERVATION_VIEWER.name())
|
||||||
|
.orElseThrow();
|
||||||
|
String username = "m006-role-user-" + System.nanoTime();
|
||||||
|
MvcResult createUserResult = mockMvc.perform(post("/api/admin/users")
|
||||||
|
.header("Authorization", "Bearer " + token)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content("""
|
||||||
|
{
|
||||||
|
"username": "%s",
|
||||||
|
"initial_password": "User@123456",
|
||||||
|
"display_name": "角色校验用户",
|
||||||
|
"user_status": "ACTIVE",
|
||||||
|
"role_ids": ["%s"],
|
||||||
|
"hotel_ids": ["HOTEL-TEST"],
|
||||||
|
"default_hotel_id": "HOTEL-TEST"
|
||||||
|
}
|
||||||
|
""".formatted(username, viewerRole.getId())))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andReturn();
|
||||||
|
String userId = objectMapper.readTree(createUserResult.getResponse().getContentAsString()).path("id").asText();
|
||||||
|
|
||||||
|
MvcResult disabledRoleResult = mockMvc.perform(post("/api/admin/roles")
|
||||||
|
.header("Authorization", "Bearer " + token)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content("""
|
||||||
|
{
|
||||||
|
"role_code": "%s",
|
||||||
|
"role_name": "M006 停用角色",
|
||||||
|
"role_status": "DISABLED"
|
||||||
|
}
|
||||||
|
""".formatted("M006_DISABLED_ROLE_" + System.nanoTime())))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.role_status").value(PlatformRoleStatus.DISABLED.name()))
|
||||||
|
.andReturn();
|
||||||
|
String disabledRoleId = objectMapper.readTree(disabledRoleResult.getResponse().getContentAsString())
|
||||||
|
.path("id").asText();
|
||||||
|
|
||||||
|
mockMvc.perform(put("/api/admin/users/{userId}/roles", userId)
|
||||||
|
.header("Authorization", "Bearer " + token)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content("""
|
||||||
|
{
|
||||||
|
"role_ids": ["%s"]
|
||||||
|
}
|
||||||
|
""".formatted(disabledRoleId)))
|
||||||
|
.andExpect(status().isBadRequest())
|
||||||
|
.andExpect(jsonPath("$.error_code").value("ADMIN_INVALID_REQUEST"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldManageCustomRoleAndRejectBuiltinRoleMutation() throws Exception {
|
void shouldManageCustomRoleAndRejectBuiltinRoleMutation() throws Exception {
|
||||||
String token = tokenFrom(login("m006-admin", "Admin@123456"));
|
String token = tokenFrom(login("m006-admin", "Admin@123456"));
|
||||||
@@ -297,6 +351,18 @@ class AdminReadonlyControllerTest {
|
|||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.permissions[0].permission_code").value("SYSTEM_USER_MANAGE"));
|
.andExpect(jsonPath("$.permissions[0].permission_code").value("SYSTEM_USER_MANAGE"));
|
||||||
|
|
||||||
|
PlatformPermissionEntity disabledPermission = createDisabledPermission("M006_DISABLED_PERMISSION_");
|
||||||
|
mockMvc.perform(put("/api/admin/roles/{roleId}/permissions", roleId)
|
||||||
|
.header("Authorization", "Bearer " + token)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content("""
|
||||||
|
{
|
||||||
|
"permission_ids": ["%s"]
|
||||||
|
}
|
||||||
|
""".formatted(disabledPermission.getId())))
|
||||||
|
.andExpect(status().isBadRequest())
|
||||||
|
.andExpect(jsonPath("$.error_code").value("ADMIN_INVALID_REQUEST"));
|
||||||
|
|
||||||
PlatformRoleEntity builtinRole = accessRepository.findRoleByCode(PlatformRoleCode.SYSTEM_ADMIN.name())
|
PlatformRoleEntity builtinRole = accessRepository.findRoleByCode(PlatformRoleCode.SYSTEM_ADMIN.name())
|
||||||
.orElseThrow();
|
.orElseThrow();
|
||||||
mockMvc.perform(put("/api/admin/roles/{roleId}", builtinRole.getId().toString())
|
mockMvc.perform(put("/api/admin/roles/{roleId}", builtinRole.getId().toString())
|
||||||
@@ -344,7 +410,7 @@ class AdminReadonlyControllerTest {
|
|||||||
{
|
{
|
||||||
"menu_name": "未来菜单更新",
|
"menu_name": "未来菜单更新",
|
||||||
"menu_type": "PAGE",
|
"menu_type": "PAGE",
|
||||||
"route_path": "/system/users",
|
"route_path": "/system/audits",
|
||||||
"permission_code": "SYSTEM_USER_MANAGE",
|
"permission_code": "SYSTEM_USER_MANAGE",
|
||||||
"visible": true,
|
"visible": true,
|
||||||
"menu_status": "ACTIVE"
|
"menu_status": "ACTIVE"
|
||||||
@@ -353,6 +419,25 @@ class AdminReadonlyControllerTest {
|
|||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.menu_code").value(menuCode))
|
.andExpect(jsonPath("$.menu_code").value(menuCode))
|
||||||
.andExpect(jsonPath("$.known_route").value(true));
|
.andExpect(jsonPath("$.known_route").value(true));
|
||||||
|
|
||||||
|
PlatformPermissionEntity disabledPermission = createDisabledPermission("M006_MENU_DISABLED_PERMISSION_");
|
||||||
|
mockMvc.perform(post("/api/admin/menus")
|
||||||
|
.header("Authorization", "Bearer " + token)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content("""
|
||||||
|
{
|
||||||
|
"menu_code": "%s",
|
||||||
|
"menu_name": "停用权限菜单",
|
||||||
|
"menu_type": "PAGE",
|
||||||
|
"route_path": "/system/users",
|
||||||
|
"permission_code": "%s",
|
||||||
|
"visible": true,
|
||||||
|
"menu_status": "ACTIVE"
|
||||||
|
}
|
||||||
|
""".formatted("M006_MENU_DISABLED_" + System.nanoTime(),
|
||||||
|
disabledPermission.getPermissionCode())))
|
||||||
|
.andExpect(status().isBadRequest())
|
||||||
|
.andExpect(jsonPath("$.error_code").value("ADMIN_INVALID_REQUEST"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -415,4 +500,18 @@ class AdminReadonlyControllerTest {
|
|||||||
JsonNode json = objectMapper.readTree(result.getResponse().getContentAsString());
|
JsonNode json = objectMapper.readTree(result.getResponse().getContentAsString());
|
||||||
return json.path("access_token").asText();
|
return json.path("access_token").asText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PlatformPermissionEntity createDisabledPermission(String codePrefix) {
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
PlatformPermissionEntity permission = new PlatformPermissionEntity();
|
||||||
|
permission.setPermissionCode(codePrefix + System.nanoTime());
|
||||||
|
permission.setPermissionName("M006 停用权限");
|
||||||
|
permission.setPermissionGroup("SYSTEM");
|
||||||
|
permission.setPermissionStatus(PlatformPermissionStatus.DISABLED.name());
|
||||||
|
permission.setSystemBuiltin(false);
|
||||||
|
permission.setCreatedAt(now);
|
||||||
|
permission.setUpdatedAt(now);
|
||||||
|
accessRepository.insertPermission(permission);
|
||||||
|
return permission;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user