模块权限修订
This commit is contained in:
@@ -910,6 +910,26 @@ public class GroupAuthzService implements IGroupAuthzService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 子组继承闸门:判定「来源组 authority 对 moduleId 的记录」是否对<b>本组</b> selfGroupId 生效。
|
||||||
|
*
|
||||||
|
* <p>规则(修复「父级未勾子组继承却仍被继承」的 bug):
|
||||||
|
* ① 来源组 == 本组:本组自身勾选的模块直接生效——{@code inheritFlag} 只约束是否下放到<b>子组</b>,不影响本组自己;
|
||||||
|
* ② 来源组为祖先(往上回溯得来):必须勾选了「子组继承」({@code inheritFlag=true}) 才会下放到本组,
|
||||||
|
* 否则该模块不继承。即「只过滤勾选了子组继承的模块;若来源组对该应用所有模块都没勾子组继承,则本组对该应用返回空」。
|
||||||
|
*/
|
||||||
|
private boolean isPermEffectiveForSelf(Integer authority, Integer selfGroupId,
|
||||||
|
Map<Integer, SysGroupModulePerm> permMap) {
|
||||||
|
SysGroupModulePerm perm = (permMap == null) ? null : permMap.get(authority);
|
||||||
|
if (perm == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (authority.equals(selfGroupId)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return Boolean.TRUE.equals(perm.getInheritFlag());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户维度权限判定:用户对指定模块是否有权限(就近原则 + 学校截止 + 角色授权并集 + 超管兜底)。
|
* 用户维度权限判定:用户对指定模块是否有权限(就近原则 + 学校截止 + 角色授权并集 + 超管兜底)。
|
||||||
*
|
*
|
||||||
@@ -921,6 +941,10 @@ public class GroupAuthzService implements IGroupAuthzService {
|
|||||||
* ② 本组无配置(如英语组为空)→ 取<b>最近</b>的有配置祖先(如教师组)为来源,不再越级到更远的学校组;
|
* ② 本组无配置(如英语组为空)→ 取<b>最近</b>的有配置祖先(如教师组)为来源,不再越级到更远的学校组;
|
||||||
* ③ 来源组的模块集合即最终集合,更上层同应用模块(如学校组多勾的模块8)一律不合并。
|
* ③ 来源组的模块集合即最终集合,更上层同应用模块(如学校组多勾的模块8)一律不合并。
|
||||||
*
|
*
|
||||||
|
* <p><b>子组继承闸门</b>(修复「父级未勾子组继承仍被继承」的 bug):来源组若是本组,本组勾选的模块直接生效;
|
||||||
|
* 来源组若是祖先,必须该模块记录 {@code inheritFlag=true}(勾选子组继承)才下放到本组,否则不继承——
|
||||||
|
* 即「只过滤勾选了子组继承的模块;若来源组对该应用所有模块都没勾子组继承,则本组对该应用返回空」。
|
||||||
|
*
|
||||||
* <p><b>学校截止</b>:祖先链只查到第一个学校组(含)为止,不跨学校到教育局/根。
|
* <p><b>学校截止</b>:祖先链只查到第一个学校组(含)为止,不跨学校到教育局/根。
|
||||||
*
|
*
|
||||||
* <p><b>其余</b>:归属组需已开通该模块所属应用(双保险,防越权)。
|
* <p><b>其余</b>:归属组需已开通该模块所属应用(双保险,防越权)。
|
||||||
@@ -947,8 +971,9 @@ public class GroupAuthzService implements IGroupAuthzService {
|
|||||||
if (authority == null) {
|
if (authority == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// 权限来源组对该模块有显式记录 → 许可;否则(来源组未勾此模块)不许可
|
// 来源组对该模块有显式记录,且(来源是本组 或 来源是祖先且勾选了子组继承)→ 许可;
|
||||||
if (permMap != null && permMap.get(authority) != null) {
|
// 否则(来源组未勾此模块 / 祖先未开启子组继承)不许可。
|
||||||
|
if (isPermEffectiveForSelf(authority, selfGroupId, permMap)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1233,7 +1258,7 @@ public class GroupAuthzService implements IGroupAuthzService {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Map<Integer, SysGroupModulePerm> pm = modulePermMap.get(moduleId);
|
Map<Integer, SysGroupModulePerm> pm = modulePermMap.get(moduleId);
|
||||||
if (pm != null && pm.get(authority) != null) {
|
if (isPermEffectiveForSelf(authority, selfGroupId, pm)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,492 @@
|
|||||||
|
package com.yunhai.userbase.service.impl;
|
||||||
|
|
||||||
|
import com.yunhai.userbase.entity.AppCategory;
|
||||||
|
import com.yunhai.userbase.entity.AppModule;
|
||||||
|
import com.yunhai.userbase.entity.ApplicationInfo;
|
||||||
|
import com.yunhai.userbase.entity.SysGroup;
|
||||||
|
import com.yunhai.userbase.entity.SysGroupAppRel;
|
||||||
|
import com.yunhai.userbase.entity.SysGroupModulePerm;
|
||||||
|
import com.yunhai.userbase.entity.SysUser;
|
||||||
|
import com.yunhai.userbase.entity.SysUserGroupRel;
|
||||||
|
import com.yunhai.userbase.enums.GroupTypeEnum;
|
||||||
|
import com.yunhai.userbase.enums.UserTypeEnum;
|
||||||
|
import com.yunhai.userbase.model.groupauthz.UserPermissionCategoryVo;
|
||||||
|
import com.yunhai.userbase.model.groupauthz.UserPermissionModuleVo;
|
||||||
|
import com.yunhai.userbase.repository.AppCategoryRepository;
|
||||||
|
import com.yunhai.userbase.repository.AppModuleRepository;
|
||||||
|
import com.yunhai.userbase.repository.ApplicationInfoRepository;
|
||||||
|
import com.yunhai.userbase.repository.SysAppRoleModuleRelRepository;
|
||||||
|
import com.yunhai.userbase.repository.SysAppRoleRepository;
|
||||||
|
import com.yunhai.userbase.repository.SysGroupAppRelRepository;
|
||||||
|
import com.yunhai.userbase.repository.SysGroupModulePermRepository;
|
||||||
|
import com.yunhai.userbase.repository.SysGroupRepository;
|
||||||
|
import com.yunhai.userbase.repository.SysUserAppRoleRelRepository;
|
||||||
|
import com.yunhai.userbase.repository.SysUserGroupRelRepository;
|
||||||
|
import com.yunhai.userbase.repository.SysUserRepository;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyList;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GroupAuthzService 权限计算单元测试。
|
||||||
|
* 重点验证「就近原则 + 子组继承闸门」:沿组的祖先链向上,取<b>最近</b>的有显式模块配置的组作为该应用唯一权限来源,
|
||||||
|
* 只返回该来源组的模块集合(不向上合并更远的祖先);且<b>来源组若为祖先,必须勾选子组继承(inheritFlag=true)才下放</b>
|
||||||
|
* (修复「子组只勾1个却继承学校全部」「返回3个而非最近祖先的2个」「父级未勾子组继承仍被继承」三类 bug)。
|
||||||
|
*/
|
||||||
|
class GroupAuthzServiceTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private GroupAuthzService groupAuthzService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private SysGroupAppRelRepository sysGroupAppRelRepository;
|
||||||
|
@Mock
|
||||||
|
private SysGroupModulePermRepository sysGroupModulePermRepository;
|
||||||
|
@Mock
|
||||||
|
private SysGroupRepository sysGroupRepository;
|
||||||
|
@Mock
|
||||||
|
private ApplicationInfoRepository applicationInfoRepository;
|
||||||
|
@Mock
|
||||||
|
private AppCategoryRepository appCategoryRepository;
|
||||||
|
@Mock
|
||||||
|
private AppModuleRepository appModuleRepository;
|
||||||
|
@Mock
|
||||||
|
private SysUserGroupRelRepository sysUserGroupRelRepository;
|
||||||
|
@Mock
|
||||||
|
private SysUserRepository sysUserRepository;
|
||||||
|
@Mock
|
||||||
|
private SysAppRoleRepository sysAppRoleRepository;
|
||||||
|
@Mock
|
||||||
|
private SysAppRoleModuleRelRepository sysAppRoleModuleRelRepository;
|
||||||
|
@Mock
|
||||||
|
private SysUserAppRoleRelRepository sysUserAppRoleRelRepository;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
MockitoAnnotations.openMocks(this);
|
||||||
|
initGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造一组固定层级:
|
||||||
|
* 5 教育云中心(根)
|
||||||
|
* └─ 27 信阳教育局
|
||||||
|
* └─ 16 云海中学(学校)
|
||||||
|
* ├─ 29 教师组
|
||||||
|
* │ └─ 31 英语组
|
||||||
|
* └─ 30 学生组
|
||||||
|
*/
|
||||||
|
private void initGroups() {
|
||||||
|
when(sysGroupRepository.findAll()).thenReturn(Arrays.asList(
|
||||||
|
group(5, "教育云中心", 0, GroupTypeEnum.NORMAL_GROUP),
|
||||||
|
group(27, "信阳教育局", 5, GroupTypeEnum.AREA_GROUP),
|
||||||
|
group(16, "云海中学", 27, GroupTypeEnum.SCHOOL_GROUP),
|
||||||
|
group(28, "信阳高级中学", 27, GroupTypeEnum.SCHOOL_GROUP),
|
||||||
|
group(29, "教师组", 16, GroupTypeEnum.TEACHER_GROUP),
|
||||||
|
group(30, "学生组", 16, GroupTypeEnum.STUDENT_GROUP),
|
||||||
|
group(31, "英语组", 29, GroupTypeEnum.NORMAL_GROUP)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void findUserPermissionTree_whenSelfGroupConfiguredApp_shouldNotInheritAncestorModules() {
|
||||||
|
// user 1 仅属于英语组 31
|
||||||
|
when(sysUserRepository.findById(1)).thenReturn(Optional.of(user(1, UserTypeEnum.NORMAL)));
|
||||||
|
when(sysUserGroupRelRepository.findByUserId(1)).thenReturn(
|
||||||
|
Collections.singletonList(rel(1, 31, 1)));
|
||||||
|
|
||||||
|
// 祖先链上的应用开通:只有学校 16 开通了 100003
|
||||||
|
when(sysGroupAppRelRepository.findByGroupIdIn(anyList())).thenAnswer(inv -> {
|
||||||
|
List<Integer> ids = inv.getArgument(0);
|
||||||
|
if (ids.contains(16)) {
|
||||||
|
return Collections.singletonList(appRel(16, 100003));
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
});
|
||||||
|
|
||||||
|
ApplicationInfo app = new ApplicationInfo();
|
||||||
|
app.setId(100003);
|
||||||
|
app.setName("通知公告");
|
||||||
|
app.setAppKey(100002);
|
||||||
|
app.setCategoryId(2);
|
||||||
|
when(applicationInfoRepository.findAllById(any())).thenReturn(Collections.singletonList(app));
|
||||||
|
|
||||||
|
AppCategory category = new AppCategory();
|
||||||
|
category.setId(2);
|
||||||
|
category.setName("家校互动");
|
||||||
|
category.setSortNo(2);
|
||||||
|
when(appCategoryRepository.findAllByOrderBySortNoAsc()).thenReturn(Collections.singletonList(category));
|
||||||
|
|
||||||
|
AppModule m8 = module(8, 100003, "已收通知");
|
||||||
|
AppModule m9 = module(9, 100003, "已发通知");
|
||||||
|
AppModule m10 = module(10, 100003, "全部通知");
|
||||||
|
when(appModuleRepository.findByAppIdIn(anyList())).thenReturn(Arrays.asList(m8, m9, m10));
|
||||||
|
|
||||||
|
// 权限记录:英语组 31 只有模块 8;学校 16 有 8/9/10 且都允许继承
|
||||||
|
when(sysGroupModulePermRepository.findByModuleIdInAndGroupIdIn(anyList(), anyList()))
|
||||||
|
.thenReturn(Arrays.asList(
|
||||||
|
perm(31, 8, true),
|
||||||
|
perm(16, 8, true),
|
||||||
|
perm(16, 9, true),
|
||||||
|
perm(16, 10, true)));
|
||||||
|
|
||||||
|
List<UserPermissionCategoryVo> result = groupAuthzService.findUserPermissionTree(31, 1);
|
||||||
|
|
||||||
|
assertEquals(1, result.size(), "应只有一个分类");
|
||||||
|
assertEquals("家校互动", result.get(0).getName());
|
||||||
|
assertEquals(1, result.get(0).getApps().size(), "应只有一个应用");
|
||||||
|
assertEquals(1, result.get(0).getApps().get(0).getModules().size(), "只应返回模块 8");
|
||||||
|
assertEquals(8, result.get(0).getApps().get(0).getModules().get(0).getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void findUserPermissionTree_whenSelfGroupNotConfiguredApp_shouldInheritFromSchool() {
|
||||||
|
// user 2 仅属于学生组 30,学生组没有配置通知公告应用的任何模块
|
||||||
|
when(sysUserRepository.findById(2)).thenReturn(Optional.of(user(2, UserTypeEnum.NORMAL)));
|
||||||
|
when(sysUserGroupRelRepository.findByUserId(2)).thenReturn(
|
||||||
|
Collections.singletonList(rel(2, 30, 1)));
|
||||||
|
|
||||||
|
when(sysGroupAppRelRepository.findByGroupIdIn(anyList())).thenAnswer(inv -> {
|
||||||
|
List<Integer> ids = inv.getArgument(0);
|
||||||
|
if (ids.contains(16)) {
|
||||||
|
return Collections.singletonList(appRel(16, 100003));
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
});
|
||||||
|
|
||||||
|
ApplicationInfo app = new ApplicationInfo();
|
||||||
|
app.setId(100003);
|
||||||
|
app.setName("通知公告");
|
||||||
|
app.setAppKey(100002);
|
||||||
|
app.setCategoryId(2);
|
||||||
|
when(applicationInfoRepository.findAllById(any())).thenReturn(Collections.singletonList(app));
|
||||||
|
|
||||||
|
AppCategory category = new AppCategory();
|
||||||
|
category.setId(2);
|
||||||
|
category.setName("家校互动");
|
||||||
|
category.setSortNo(2);
|
||||||
|
when(appCategoryRepository.findAllByOrderBySortNoAsc()).thenReturn(Collections.singletonList(category));
|
||||||
|
|
||||||
|
AppModule m8 = module(8, 100003, "已收通知");
|
||||||
|
AppModule m9 = module(9, 100003, "已发通知");
|
||||||
|
AppModule m10 = module(10, 100003, "全部通知");
|
||||||
|
when(appModuleRepository.findByAppIdIn(anyList())).thenReturn(Arrays.asList(m8, m9, m10));
|
||||||
|
|
||||||
|
// 学校 16 三个模块都开放继承;学生组 30 没有任何记录
|
||||||
|
when(sysGroupModulePermRepository.findByModuleIdInAndGroupIdIn(anyList(), anyList()))
|
||||||
|
.thenReturn(Arrays.asList(
|
||||||
|
perm(16, 8, true),
|
||||||
|
perm(16, 9, true),
|
||||||
|
perm(16, 10, true)));
|
||||||
|
|
||||||
|
List<UserPermissionCategoryVo> result = groupAuthzService.findUserPermissionTree(30, 2);
|
||||||
|
|
||||||
|
assertEquals(3, result.get(0).getApps().get(0).getModules().size(),
|
||||||
|
"未配置应用时,应继承学校的全部 3 个模块");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void hasModulePermission_whenSelfGroupConfiguredApp_onlySelfRecordsCount() {
|
||||||
|
AppModule m8 = module(8, 100003, "已收通知");
|
||||||
|
AppModule m9 = module(9, 100003, "已发通知");
|
||||||
|
AppModule m10 = module(10, 100003, "全部通知");
|
||||||
|
when(appModuleRepository.findById(9)).thenReturn(Optional.of(m9));
|
||||||
|
when(sysUserAppRoleRelRepository.findByUserId(1)).thenReturn(Collections.emptyList());
|
||||||
|
|
||||||
|
when(sysUserGroupRelRepository.findByUserId(1)).thenReturn(
|
||||||
|
Collections.singletonList(rel(1, 31, 1)));
|
||||||
|
|
||||||
|
when(sysGroupAppRelRepository.existsByGroupIdAndAppId(31, 100003)).thenReturn(true);
|
||||||
|
when(appModuleRepository.findByAppIdIn(Collections.singletonList(100003))).thenReturn(Arrays.asList(m8, m9, m10));
|
||||||
|
|
||||||
|
// 英语组 31 配置了应用 100003(模块 8);学校 16 有模块 9(但 31 已就近接管,不应继承)
|
||||||
|
when(sysGroupModulePermRepository.findByModuleIdInAndGroupIdIn(
|
||||||
|
Arrays.asList(8, 9, 10), Arrays.asList(31, 29, 16, 27, 5)))
|
||||||
|
.thenReturn(Arrays.asList(perm(31, 8, true), perm(16, 9, true)));
|
||||||
|
|
||||||
|
assertFalse(groupAuthzService.hasModulePermission(1, 9),
|
||||||
|
"英语组已配置通知公告应用(就近接管),不应再继承学校的已发通知模块");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void hasModulePermission_whenSelfGroupHasTheModule_shouldReturnTrue() {
|
||||||
|
AppModule m8 = module(8, 100003, "已收通知");
|
||||||
|
AppModule m9 = module(9, 100003, "已发通知");
|
||||||
|
AppModule m10 = module(10, 100003, "全部通知");
|
||||||
|
when(appModuleRepository.findById(8)).thenReturn(Optional.of(m8));
|
||||||
|
when(sysUserAppRoleRelRepository.findByUserId(1)).thenReturn(Collections.emptyList());
|
||||||
|
|
||||||
|
when(sysUserGroupRelRepository.findByUserId(1)).thenReturn(
|
||||||
|
Collections.singletonList(rel(1, 31, 1)));
|
||||||
|
|
||||||
|
when(sysGroupAppRelRepository.existsByGroupIdAndAppId(31, 100003)).thenReturn(true);
|
||||||
|
when(appModuleRepository.findByAppIdIn(Collections.singletonList(100003))).thenReturn(Arrays.asList(m8, m9, m10));
|
||||||
|
|
||||||
|
when(sysGroupModulePermRepository.findByModuleIdInAndGroupIdIn(
|
||||||
|
Arrays.asList(8, 9, 10), Arrays.asList(31, 29, 16, 27, 5)))
|
||||||
|
.thenReturn(Arrays.asList(perm(31, 8, true), perm(16, 8, true)));
|
||||||
|
|
||||||
|
assertTrue(groupAuthzService.hasModulePermission(1, 8),
|
||||||
|
"英语组自身已配置模块 8,应返回 true");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void findUserPermissionTree_whenSelfGroupEmpty_shouldUseNearestAncestorTeacherGroup() {
|
||||||
|
// user 1 仅属于英语组 31;英语组(31)无任何模块授权,教师组(29)有模块9/10,学校(16)有8/9/10
|
||||||
|
when(sysUserRepository.findById(1)).thenReturn(Optional.of(user(1, UserTypeEnum.NORMAL)));
|
||||||
|
when(sysUserGroupRelRepository.findByUserId(1)).thenReturn(
|
||||||
|
Collections.singletonList(rel(1, 31, 1)));
|
||||||
|
|
||||||
|
when(sysGroupAppRelRepository.findByGroupIdIn(anyList())).thenAnswer(inv -> {
|
||||||
|
List<Integer> ids = inv.getArgument(0);
|
||||||
|
if (ids.contains(16)) {
|
||||||
|
return Collections.singletonList(appRel(16, 100003));
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
});
|
||||||
|
|
||||||
|
ApplicationInfo app = new ApplicationInfo();
|
||||||
|
app.setId(100003);
|
||||||
|
app.setName("通知公告");
|
||||||
|
app.setAppKey(100002);
|
||||||
|
app.setCategoryId(2);
|
||||||
|
when(applicationInfoRepository.findAllById(any())).thenReturn(Collections.singletonList(app));
|
||||||
|
|
||||||
|
AppCategory category = new AppCategory();
|
||||||
|
category.setId(2);
|
||||||
|
category.setName("家校互动");
|
||||||
|
category.setSortNo(2);
|
||||||
|
when(appCategoryRepository.findAllByOrderBySortNoAsc()).thenReturn(Collections.singletonList(category));
|
||||||
|
|
||||||
|
AppModule m8 = module(8, 100003, "已收通知");
|
||||||
|
AppModule m9 = module(9, 100003, "已发通知");
|
||||||
|
AppModule m10 = module(10, 100003, "全部通知");
|
||||||
|
when(appModuleRepository.findByAppIdIn(anyList())).thenReturn(Arrays.asList(m8, m9, m10));
|
||||||
|
|
||||||
|
// 英语组31空;教师组29有9/10;学校16有8/9/10
|
||||||
|
when(sysGroupModulePermRepository.findByModuleIdInAndGroupIdIn(anyList(), anyList()))
|
||||||
|
.thenReturn(Arrays.asList(
|
||||||
|
perm(29, 9, true),
|
||||||
|
perm(29, 10, true),
|
||||||
|
perm(16, 8, true),
|
||||||
|
perm(16, 9, true),
|
||||||
|
perm(16, 10, true)));
|
||||||
|
|
||||||
|
List<UserPermissionCategoryVo> result = groupAuthzService.findUserPermissionTree(31, 1);
|
||||||
|
|
||||||
|
assertEquals(1, result.size(), "应只有一个分类");
|
||||||
|
assertEquals(1, result.get(0).getApps().size(), "应只有一个应用");
|
||||||
|
assertEquals(2, result.get(0).getApps().get(0).getModules().size(),
|
||||||
|
"本组空→取最近教师组29的2个模块,不应合并学校组多出的模块8");
|
||||||
|
Set<Integer> moduleIds = new LinkedHashSet<>();
|
||||||
|
for (UserPermissionModuleVo mv : result.get(0).getApps().get(0).getModules()) {
|
||||||
|
moduleIds.add(mv.getId());
|
||||||
|
}
|
||||||
|
assertTrue(moduleIds.contains(9) && moduleIds.contains(10) && moduleIds.size() == 2,
|
||||||
|
"应仅含模块 9 和 10,不含学校组多出的模块 8");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void findUserPermissionTree_whenNearestAncestorAllNotInheritable_returnsEmpty() {
|
||||||
|
// 英语组31空;教师组29有9/10但 inheritFlag=false(未勾子组继承);学校16有8/9/10
|
||||||
|
when(sysUserRepository.findById(1)).thenReturn(Optional.of(user(1, UserTypeEnum.NORMAL)));
|
||||||
|
when(sysUserGroupRelRepository.findByUserId(1)).thenReturn(
|
||||||
|
Collections.singletonList(rel(1, 31, 1)));
|
||||||
|
|
||||||
|
when(sysGroupAppRelRepository.findByGroupIdIn(anyList())).thenAnswer(inv -> {
|
||||||
|
List<Integer> ids = inv.getArgument(0);
|
||||||
|
if (ids.contains(16)) {
|
||||||
|
return Collections.singletonList(appRel(16, 100003));
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
});
|
||||||
|
|
||||||
|
ApplicationInfo app = new ApplicationInfo();
|
||||||
|
app.setId(100003);
|
||||||
|
app.setName("通知公告");
|
||||||
|
app.setAppKey(100002);
|
||||||
|
app.setCategoryId(2);
|
||||||
|
when(applicationInfoRepository.findAllById(any())).thenReturn(Collections.singletonList(app));
|
||||||
|
|
||||||
|
AppCategory category = new AppCategory();
|
||||||
|
category.setId(2);
|
||||||
|
category.setName("家校互动");
|
||||||
|
category.setSortNo(2);
|
||||||
|
when(appCategoryRepository.findAllByOrderBySortNoAsc()).thenReturn(Collections.singletonList(category));
|
||||||
|
|
||||||
|
AppModule m8 = module(8, 100003, "已收通知");
|
||||||
|
AppModule m9 = module(9, 100003, "已发通知");
|
||||||
|
AppModule m10 = module(10, 100003, "全部通知");
|
||||||
|
when(appModuleRepository.findByAppIdIn(anyList())).thenReturn(Arrays.asList(m8, m9, m10));
|
||||||
|
|
||||||
|
// 教师组29(最近来源)所有模块 inheritFlag=false;即便学校16开放,也不应被继承
|
||||||
|
when(sysGroupModulePermRepository.findByModuleIdInAndGroupIdIn(anyList(), anyList()))
|
||||||
|
.thenReturn(Arrays.asList(
|
||||||
|
perm(29, 9, false),
|
||||||
|
perm(29, 10, false),
|
||||||
|
perm(16, 8, true),
|
||||||
|
perm(16, 9, true),
|
||||||
|
perm(16, 10, true)));
|
||||||
|
|
||||||
|
List<UserPermissionCategoryVo> result = groupAuthzService.findUserPermissionTree(31, 1);
|
||||||
|
|
||||||
|
// 最近来源(教师组)对该应用所有模块都未勾子组继承 → 本组该应用为空,不应合并学校组
|
||||||
|
assertTrue(result.isEmpty(),
|
||||||
|
"最近祖先(教师组)未勾子组继承,应返回空,不应从学校组继承任何模块");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void findUserPermissionTree_whenNearestAncestorPartiallyInheritable_returnsOnlyInheritable() {
|
||||||
|
// 英语组31空;教师组29有9(inheritFlag=true)/10(inheritFlag=false);学校16有8/9/10
|
||||||
|
when(sysUserRepository.findById(1)).thenReturn(Optional.of(user(1, UserTypeEnum.NORMAL)));
|
||||||
|
when(sysUserGroupRelRepository.findByUserId(1)).thenReturn(
|
||||||
|
Collections.singletonList(rel(1, 31, 1)));
|
||||||
|
|
||||||
|
when(sysGroupAppRelRepository.findByGroupIdIn(anyList())).thenAnswer(inv -> {
|
||||||
|
List<Integer> ids = inv.getArgument(0);
|
||||||
|
if (ids.contains(16)) {
|
||||||
|
return Collections.singletonList(appRel(16, 100003));
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
});
|
||||||
|
|
||||||
|
ApplicationInfo app = new ApplicationInfo();
|
||||||
|
app.setId(100003);
|
||||||
|
app.setName("通知公告");
|
||||||
|
app.setAppKey(100002);
|
||||||
|
app.setCategoryId(2);
|
||||||
|
when(applicationInfoRepository.findAllById(any())).thenReturn(Collections.singletonList(app));
|
||||||
|
|
||||||
|
AppCategory category = new AppCategory();
|
||||||
|
category.setId(2);
|
||||||
|
category.setName("家校互动");
|
||||||
|
category.setSortNo(2);
|
||||||
|
when(appCategoryRepository.findAllByOrderBySortNoAsc()).thenReturn(Collections.singletonList(category));
|
||||||
|
|
||||||
|
AppModule m8 = module(8, 100003, "已收通知");
|
||||||
|
AppModule m9 = module(9, 100003, "已发通知");
|
||||||
|
AppModule m10 = module(10, 100003, "全部通知");
|
||||||
|
when(appModuleRepository.findByAppIdIn(anyList())).thenReturn(Arrays.asList(m8, m9, m10));
|
||||||
|
|
||||||
|
when(sysGroupModulePermRepository.findByModuleIdInAndGroupIdIn(anyList(), anyList()))
|
||||||
|
.thenReturn(Arrays.asList(
|
||||||
|
perm(29, 9, true),
|
||||||
|
perm(29, 10, false),
|
||||||
|
perm(16, 8, true),
|
||||||
|
perm(16, 9, true),
|
||||||
|
perm(16, 10, true)));
|
||||||
|
|
||||||
|
List<UserPermissionCategoryVo> result = groupAuthzService.findUserPermissionTree(31, 1);
|
||||||
|
|
||||||
|
assertEquals(1, result.size(), "应只有一个分类");
|
||||||
|
assertEquals(1, result.get(0).getApps().size(), "应只有一个应用");
|
||||||
|
assertEquals(1, result.get(0).getApps().get(0).getModules().size(),
|
||||||
|
"最近来源(教师组)只有模块9勾了子组继承,应只返回模块9,模块10与学校组模块8均不继承");
|
||||||
|
assertEquals(9, result.get(0).getApps().get(0).getModules().get(0).getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void hasModulePermission_whenSelfGroupEmpty_nearestAncestorIsTeacherGroup() {
|
||||||
|
// 英语组31空,教师组29有9/10,学校16有8/9/10
|
||||||
|
AppModule m8 = module(8, 100003, "已收通知");
|
||||||
|
AppModule m9 = module(9, 100003, "已发通知");
|
||||||
|
AppModule m10 = module(10, 100003, "全部通知");
|
||||||
|
when(appModuleRepository.findById(8)).thenReturn(Optional.of(m8));
|
||||||
|
when(appModuleRepository.findById(9)).thenReturn(Optional.of(m9));
|
||||||
|
when(appModuleRepository.findById(10)).thenReturn(Optional.of(m10));
|
||||||
|
when(sysUserAppRoleRelRepository.findByUserId(1)).thenReturn(Collections.emptyList());
|
||||||
|
when(sysUserGroupRelRepository.findByUserId(1)).thenReturn(
|
||||||
|
Collections.singletonList(rel(1, 31, 1)));
|
||||||
|
when(sysGroupAppRelRepository.existsByGroupIdAndAppId(31, 100003)).thenReturn(true);
|
||||||
|
when(appModuleRepository.findByAppIdIn(Collections.singletonList(100003))).thenReturn(Arrays.asList(m8, m9, m10));
|
||||||
|
when(sysGroupModulePermRepository.findByModuleIdInAndGroupIdIn(
|
||||||
|
Arrays.asList(8, 9, 10), Arrays.asList(31, 29, 16, 27, 5)))
|
||||||
|
.thenReturn(Arrays.asList(
|
||||||
|
perm(29, 9, true), perm(29, 10, true),
|
||||||
|
perm(16, 8, true), perm(16, 9, true), perm(16, 10, true)));
|
||||||
|
|
||||||
|
assertFalse(groupAuthzService.hasModulePermission(1, 8),
|
||||||
|
"英语组为空→最近来源是教师组29,学校组多勾的模块8不应被继承");
|
||||||
|
assertTrue(groupAuthzService.hasModulePermission(1, 9),
|
||||||
|
"教师组29勾选的模块9应可访问");
|
||||||
|
assertTrue(groupAuthzService.hasModulePermission(1, 10),
|
||||||
|
"教师组29勾选的模块10应可访问");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================== helper =====================
|
||||||
|
|
||||||
|
private SysGroup group(Integer id, String name, Integer parentId, GroupTypeEnum type) {
|
||||||
|
SysGroup g = new SysGroup();
|
||||||
|
g.setId(id);
|
||||||
|
g.setName(name);
|
||||||
|
g.setParentId(parentId);
|
||||||
|
g.setType(type);
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SysUser user(Integer id, UserTypeEnum type) {
|
||||||
|
SysUser u = new SysUser();
|
||||||
|
u.setId(id);
|
||||||
|
u.setType(type);
|
||||||
|
u.setAccount("u" + id);
|
||||||
|
u.setRealName("用户" + id);
|
||||||
|
return u;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SysUserGroupRel rel(Integer userId, Integer groupId, Integer isMain) {
|
||||||
|
SysUserGroupRel r = new SysUserGroupRel();
|
||||||
|
r.setUserId(userId);
|
||||||
|
r.setGroupId(groupId);
|
||||||
|
r.setIsMain(isMain);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SysGroupAppRel appRel(Integer groupId, Integer appId) {
|
||||||
|
SysGroupAppRel r = new SysGroupAppRel();
|
||||||
|
r.setGroupId(groupId);
|
||||||
|
r.setAppId(appId);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SysGroupModulePerm perm(Integer groupId, Integer moduleId, boolean inherit) {
|
||||||
|
SysGroupModulePerm p = new SysGroupModulePerm();
|
||||||
|
p.setGroupId(groupId);
|
||||||
|
p.setModuleId(moduleId);
|
||||||
|
p.setInheritFlag(inherit);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ApplicationInfo app(Integer id, String name, Integer categoryId) {
|
||||||
|
ApplicationInfo a = new ApplicationInfo();
|
||||||
|
a.setId(id);
|
||||||
|
a.setName(name);
|
||||||
|
a.setCategoryId(categoryId);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AppModule module(Integer id, Integer appId, String name) {
|
||||||
|
AppModule m = new AppModule();
|
||||||
|
m.setId(id);
|
||||||
|
m.setAppId(appId);
|
||||||
|
m.setName(name);
|
||||||
|
m.setCustomId(String.valueOf(id));
|
||||||
|
m.setLevel(1);
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user