问题1 学校管理员多校并存:SCHOOL 级判定原先收拢该管理员名下所有学校
(多校 = 多棵子树并集),导致一个在云海小学+云海中学都有管理员身份的账号
左侧组织树出现两个学校根。
- AccessScopeService.computeScope:SCHOOL 分支优先取 sys_user_current_role
的 schoolId(须命中「该用户确有 {校领导,管理员} 正常态身份行且组为学校组」白名单,
一并挡住脏记录指向别校、切到非管理身份两类越权)→ 无记录/不合法回落本职主校
→ 都解析不出才保留历史宽口径(范围为空会误降 NONE 被整体 403,宁宽勿窄)
- 落在 scopeGroupIds 上游:树/列表/搜索/导入导出/写口 guard 一起收敛,
避免「树里看不到但能搜到、能跨校写」的半修复
- 新增 AccessScopeServiceTest 9 例(该类此前无测试):当前身份收敛/回落主校/
脏记录与区域组回落/主组链无学校的宽口径/超管不受影响/无管理身份行不进 SCHOOL
- 类注释与《管理端权限分级改造方案》§1/§5/§6/§8 同步修订留痕(原写明与当前身份无关)
问题3 权限一览「(本组)」备注:撤销上一版「上下文组覆盖主组」的做法,
改为 own = 用户归属组数>1 且节点为主组——单组用户不再带备注,多组用户
只有本职组带标记(兼职组不带),切换查看时才具备区分意义。
This commit is contained in:
@@ -1332,12 +1332,14 @@ public class GroupAuthzService implements IGroupAuthzService {
|
||||
if (selfGroupIds.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1.5 上下文收窄:指定组且用户确属该组时只画该组链路(权限一览按行查看「用户×组」对);
|
||||
// 「本组」标记同步指向该组——主组可能在被收窄掉的链路上,沿用主组会导致整棵树没有「本组」
|
||||
// 「本组」备注只在用户归属多个组时才有区分意义(单组用户整棵树都是他的组,备注冗余);
|
||||
// 需在收窄前统计,故单独记一份
|
||||
boolean multiGroup = selfGroupIds.size() > 1;
|
||||
// 1.5 上下文收窄:指定组且用户确属该组时只画该组链路(权限一览按行查看「用户×组」对)。
|
||||
// 「本组」标记不跟随上下文组:兼职组不标,只有本职主组才标——否则切到哪个组都显示「本组」
|
||||
if (contextGroupId != null && selfGroupIds.contains(contextGroupId)) {
|
||||
selfGroupIds = new LinkedHashSet<>();
|
||||
selfGroupIds.add(contextGroupId);
|
||||
mainGroupId = contextGroupId;
|
||||
}
|
||||
// 2. 祖先链并集(含自身);多个归属组共享祖先时只出现一次
|
||||
Map<Integer, SysGroup> allGroupMap = buildAllGroupMap();
|
||||
@@ -1360,7 +1362,7 @@ public class GroupAuthzService implements IGroupAuthzService {
|
||||
UserGroupNodeVo vo = new UserGroupNodeVo();
|
||||
vo.setId(id);
|
||||
vo.setName(g == null || g.getName() == null ? String.valueOf(id) : g.getName());
|
||||
vo.setOwn(mainGroupId != null && id.equals(mainGroupId));
|
||||
vo.setOwn(multiGroup && mainGroupId != null && id.equals(mainGroupId));
|
||||
voMap.put(id, vo);
|
||||
}
|
||||
List<UserGroupNodeVo> roots = new ArrayList<>();
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.yunhai.platform.utils.exception.DException;
|
||||
import com.yunhai.userbase.common.context.UserContext;
|
||||
import com.yunhai.userbase.entity.SysGroup;
|
||||
import com.yunhai.userbase.entity.SysUser;
|
||||
import com.yunhai.userbase.entity.SysUserCurrentRole;
|
||||
import com.yunhai.userbase.entity.SysUserGroupRel;
|
||||
import com.yunhai.userbase.entity.SysUserRoleRel;
|
||||
import com.yunhai.userbase.enums.AccessLevelEnum;
|
||||
@@ -12,6 +13,7 @@ import com.yunhai.userbase.enums.StatusEnum;
|
||||
import com.yunhai.userbase.enums.SysRoleEnum;
|
||||
import com.yunhai.userbase.enums.UserTypeEnum;
|
||||
import com.yunhai.userbase.repository.SysGroupRepository;
|
||||
import com.yunhai.userbase.repository.SysUserCurrentRoleRepository;
|
||||
import com.yunhai.userbase.repository.SysUserGroupRelRepository;
|
||||
import com.yunhai.userbase.repository.SysUserRepository;
|
||||
import com.yunhai.userbase.repository.SysUserRoleRelRepository;
|
||||
@@ -36,7 +38,10 @@ import java.util.Set;
|
||||
* <li>SUPER:sys_user.type=1(内置超管账号)或基石账号 id=100000,行为与现状完全一致;</li>
|
||||
* <li>SCHOOL:sys_user_role_rel 存在正常态身份行 roleId∈{2 校领导, 7 管理员} 且其 schoolId
|
||||
* 对应组类型为学校组(schoolId 恒为真实组 id,局管理员的 schoolId=区域组,必须校验组类型
|
||||
* 防止误判)。与当前激活身份无关,不看 sys_user_current_role;</li>
|
||||
* 防止误判)。<b>范围收敛到「当前激活身份」所在学校</b>(sys_user_current_role →
|
||||
* userhome 切换身份时写入;无记录或记录不合法回落本职主校)——多校管理员同一时刻只管理
|
||||
* 一所学校,要管理另一所须先切换身份;见 {@link #computeScope} 2.1/2.2;
|
||||
* </li>
|
||||
* <li>BUREAU:主归属组链(主组 → 根)含区域组且不含学校组,不限角色;</li>
|
||||
* <li>NONE:其余(登录用户但无任何管理面)。</li>
|
||||
* </ol>
|
||||
@@ -75,6 +80,9 @@ public class AccessScopeService {
|
||||
@Autowired
|
||||
private SysGroupRepository sysGroupRepository;
|
||||
|
||||
@Autowired
|
||||
private SysUserCurrentRoleRepository sysUserCurrentRoleRepository;
|
||||
|
||||
@Autowired
|
||||
private MainGroupResolver mainGroupResolver;
|
||||
|
||||
@@ -132,6 +140,20 @@ public class AccessScopeService {
|
||||
}
|
||||
}
|
||||
if (!schoolGroupIds.isEmpty()) {
|
||||
// 2.1 收敛到「当前激活身份」所在学校:身份切换(userhome → /oapi/identity/switch → sys_user_current_role)
|
||||
// 决定本次会话管理哪一所学校——要管理其他学校需先切换身份,避免多校管理员同时看到多棵树、
|
||||
// 并在不知情的情况下跨校改动数据
|
||||
Integer activeSchoolId = resolveActiveSchoolId(userId, schoolGroupIds);
|
||||
if (activeSchoolId != null) {
|
||||
return AccessScope.school(Collections.singletonList(activeSchoolId));
|
||||
}
|
||||
// 2.2 无切换记录(或记录不指向其可管理的学校)→ 回落本职主校
|
||||
Integer mainSchoolId = resolveMainSchoolId(userId, schoolGroupIds, groupMap);
|
||||
if (mainSchoolId != null) {
|
||||
return AccessScope.school(Collections.singletonList(mainSchoolId));
|
||||
}
|
||||
// 2.3 主校也解析不出(无组关系/主组不在任何管理学校内)→ 保留历史口径(全部可管理学校),
|
||||
// 宁宽勿窄:范围为空会把 SCHOOL 级误降为 NONE,被 ManageAccessInterceptor 整体 403
|
||||
return AccessScope.school(schoolGroupIds);
|
||||
}
|
||||
// 3) BUREAU:主归属组链含区域组且不含学校组(不限角色)
|
||||
@@ -171,6 +193,41 @@ public class AccessScopeService {
|
||||
return DicUtil.SUPER_ADMIN_ID.equals(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前激活身份所在学校(sys_user_current_role):仅当该记录指向的 schoolId 属于
|
||||
* {@code adminSchoolIds}(即用户确有 {2 校领导, 7 管理员} 正常态身份行且该组为学校组)时采用,
|
||||
* 借此一次性挡住「脏记录指向别校」「切到非管理身份(如老师身份在另一校)」两类越权。<br>
|
||||
* 无切换记录 / 记录非法 → null,由调用方回落主校。
|
||||
*/
|
||||
private Integer resolveActiveSchoolId(Integer userId, List<Integer> adminSchoolIds) {
|
||||
SysUserCurrentRole current = sysUserCurrentRoleRepository.findByUserId(userId).orElse(null);
|
||||
if (current == null || current.getSchoolId() == null) {
|
||||
return null;
|
||||
}
|
||||
return adminSchoolIds.contains(current.getSchoolId()) ? current.getSchoolId() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 本职主校:主归属组(isMain=1,无主兜底 id 最小)沿祖先链找最近的学校组,且该校须在其可管理学校内。<br>
|
||||
* 解析不出(无组关系 / 主组不在学校内 / 主校非其管理学校)→ null,由调用方保留历史宽口径。
|
||||
*/
|
||||
private Integer resolveMainSchoolId(Integer userId, List<Integer> adminSchoolIds, Map<Integer, SysGroup> groupMap) {
|
||||
SysUserGroupRel mainRel = mainGroupResolver.resolve(userId).orElse(null);
|
||||
if (mainRel == null || mainRel.getGroupId() == null) {
|
||||
return null;
|
||||
}
|
||||
if (groupMap == null) {
|
||||
groupMap = buildAllGroupMap();
|
||||
}
|
||||
for (Integer gid : ancestorChain(mainRel.getGroupId(), groupMap)) {
|
||||
SysGroup g = groupMap.get(gid);
|
||||
if (g != null && g.getType() == GroupTypeEnum.SCHOOL_GROUP) {
|
||||
return adminSchoolIds.contains(gid) ? gid : null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ===================== 范围展开 =====================
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
package com.yunhai.userbase.service.resolve;
|
||||
|
||||
import com.yunhai.userbase.entity.SysGroup;
|
||||
import com.yunhai.userbase.entity.SysUser;
|
||||
import com.yunhai.userbase.entity.SysUserCurrentRole;
|
||||
import com.yunhai.userbase.entity.SysUserGroupRel;
|
||||
import com.yunhai.userbase.entity.SysUserRoleRel;
|
||||
import com.yunhai.userbase.enums.AccessLevelEnum;
|
||||
import com.yunhai.userbase.enums.GroupTypeEnum;
|
||||
import com.yunhai.userbase.enums.StatusEnum;
|
||||
import com.yunhai.userbase.enums.SysRoleEnum;
|
||||
import com.yunhai.userbase.enums.UserTypeEnum;
|
||||
import com.yunhai.userbase.repository.SysGroupRepository;
|
||||
import com.yunhai.userbase.repository.SysUserCurrentRoleRepository;
|
||||
import com.yunhai.userbase.repository.SysUserGroupRelRepository;
|
||||
import com.yunhai.userbase.repository.SysUserRepository;
|
||||
import com.yunhai.userbase.repository.SysUserRoleRelRepository;
|
||||
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.Optional;
|
||||
|
||||
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.anyInt;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* 管理端访问范围解析单测:重点是 SCHOOL 级「范围收敛到当前激活身份所在学校」。<br>
|
||||
* 规则:优先 sys_user_current_role 且该校须在其可管理学校内 → 否则回落本职主校 → 都拿不到才保留
|
||||
* 历史宽口径(全部可管理学校),宁宽勿窄(范围为空会把 SCHOOL 误降为 NONE 而被整体 403)。
|
||||
*/
|
||||
class AccessScopeServiceTest {
|
||||
|
||||
private static final Integer USER_ID = 1;
|
||||
/** 区域组 */
|
||||
private static final Integer AREA_ID = 5;
|
||||
/** 学校组:云海小学 */
|
||||
private static final Integer SCHOOL_A = 10;
|
||||
/** 学校组:云海中学 */
|
||||
private static final Integer SCHOOL_B = 20;
|
||||
/** 普通组(云海小学下的教师组) */
|
||||
private static final Integer GROUP_A_TEACHER = 101;
|
||||
|
||||
@InjectMocks
|
||||
private AccessScopeService accessScopeService;
|
||||
|
||||
@Mock
|
||||
private SysUserRepository sysUserRepository;
|
||||
@Mock
|
||||
private SysUserRoleRelRepository sysUserRoleRelRepository;
|
||||
@Mock
|
||||
private SysUserGroupRelRepository sysUserGroupRelRepository;
|
||||
@Mock
|
||||
private SysGroupRepository sysGroupRepository;
|
||||
@Mock
|
||||
private SysUserCurrentRoleRepository sysUserCurrentRoleRepository;
|
||||
@Mock
|
||||
private MainGroupResolver mainGroupResolver;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
// 组快照:区域组(5) → 学校组(10/20) → 教师组(101,挂在小学下)
|
||||
when(sysGroupRepository.findAll()).thenReturn(Arrays.asList(
|
||||
group(SCHOOL_A, "云海小学", GROUP_A_TEACHER - 100, GroupTypeEnum.SCHOOL_GROUP),
|
||||
group(SCHOOL_B, "云海中学", AREA_ID, GroupTypeEnum.SCHOOL_GROUP),
|
||||
group(AREA_ID, "信阳教育局", 0, GroupTypeEnum.AREA_GROUP),
|
||||
group(GROUP_A_TEACHER, "教师组", SCHOOL_A, GroupTypeEnum.TEACHER_GROUP)
|
||||
));
|
||||
}
|
||||
|
||||
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 normalUser() {
|
||||
SysUser user = new SysUser();
|
||||
user.setId(USER_ID);
|
||||
user.setType(UserTypeEnum.SCHOOL_ADMIN);
|
||||
return user;
|
||||
}
|
||||
|
||||
/** 学校管理员身份行(roleId=7 管理员 + 指定学校) */
|
||||
private SysUserRoleRel schoolAdminRel(Integer schoolId) {
|
||||
SysUserRoleRel rel = new SysUserRoleRel();
|
||||
rel.setUserId(USER_ID);
|
||||
rel.setRoleId(SysRoleEnum.ADMIN.getKey());
|
||||
rel.setSchoolId(schoolId);
|
||||
rel.setStatus(StatusEnum.NORMAL);
|
||||
return rel;
|
||||
}
|
||||
|
||||
private SysUserGroupRel groupRel(Integer groupId, Integer isMain) {
|
||||
SysUserGroupRel rel = new SysUserGroupRel();
|
||||
rel.setId(1);
|
||||
rel.setUserId(USER_ID);
|
||||
rel.setGroupId(groupId);
|
||||
rel.setIsMain(isMain);
|
||||
return rel;
|
||||
}
|
||||
|
||||
private SysUserCurrentRole currentRole(Integer schoolId) {
|
||||
SysUserCurrentRole cur = new SysUserCurrentRole();
|
||||
cur.setUserId(USER_ID);
|
||||
cur.setSchoolId(schoolId);
|
||||
cur.setRoleId(SysRoleEnum.ADMIN.getKey());
|
||||
return cur;
|
||||
}
|
||||
|
||||
/** 公共桩:平台用户 + 两所学校的管理员身份行 */
|
||||
private void givenTwoSchoolAdmin() {
|
||||
when(sysUserRepository.findById(USER_ID)).thenReturn(Optional.of(normalUser()));
|
||||
when(sysUserRoleRelRepository.findByUserIdAndStatus(USER_ID, StatusEnum.NORMAL))
|
||||
.thenReturn(Arrays.asList(schoolAdminRel(SCHOOL_A), schoolAdminRel(SCHOOL_B)));
|
||||
}
|
||||
|
||||
private AccessScope compute() {
|
||||
// computeScope(Integer) 为包内可见:绕过 UserContext 直接单测判定逻辑
|
||||
return accessScopeService.computeScope(USER_ID);
|
||||
}
|
||||
|
||||
// ===================== 收敛到当前激活身份 =====================
|
||||
|
||||
/** 当前身份在小学 → 范围只有小学(即使此人也是中学管理员) */
|
||||
@Test
|
||||
void computeScope_whenCurrentRoleIsSchoolB_shouldNarrowToSchoolB() {
|
||||
givenTwoSchoolAdmin();
|
||||
when(sysUserCurrentRoleRepository.findByUserId(USER_ID)).thenReturn(Optional.of(currentRole(SCHOOL_B)));
|
||||
|
||||
AccessScope scope = compute();
|
||||
|
||||
assertEquals(AccessLevelEnum.SCHOOL, scope.getLevel());
|
||||
assertEquals(Collections.singletonList(SCHOOL_B), scope.schoolGroupIds());
|
||||
}
|
||||
|
||||
/** 无切换记录 → 回落本职主校(主组在小学下 → 只保留小学) */
|
||||
@Test
|
||||
void computeScope_whenNoCurrentRole_shouldFallbackToMainSchool() {
|
||||
givenTwoSchoolAdmin();
|
||||
when(sysUserCurrentRoleRepository.findByUserId(USER_ID)).thenReturn(Optional.empty());
|
||||
when(mainGroupResolver.resolve(USER_ID)).thenReturn(Optional.of(groupRel(GROUP_A_TEACHER, 1)));
|
||||
|
||||
AccessScope scope = compute();
|
||||
|
||||
assertEquals(AccessLevelEnum.SCHOOL, scope.getLevel());
|
||||
assertEquals(Collections.singletonList(SCHOOL_A), scope.schoolGroupIds());
|
||||
}
|
||||
|
||||
// ===================== 非法/脏记录的兜底 =====================
|
||||
|
||||
/** 当前身份指向非管理学校(如切到老师身份的别校)→ 不认,回落主校 */
|
||||
@Test
|
||||
void computeScope_whenCurrentRoleSchoolNotAdministered_shouldFallbackToMainSchool() {
|
||||
givenTwoSchoolAdmin();
|
||||
when(sysUserCurrentRoleRepository.findByUserId(USER_ID))
|
||||
.thenReturn(Optional.of(currentRole(999)));
|
||||
when(mainGroupResolver.resolve(USER_ID)).thenReturn(Optional.of(groupRel(GROUP_A_TEACHER, 1)));
|
||||
|
||||
assertEquals(Collections.singletonList(SCHOOL_A), compute().schoolGroupIds());
|
||||
}
|
||||
|
||||
/** 当前身份指向区域组(局端身份)→ 不属于其可管理学校,回落主校 */
|
||||
@Test
|
||||
void computeScope_whenCurrentRoleIsAreaGroup_shouldFallbackToMainSchool() {
|
||||
givenTwoSchoolAdmin();
|
||||
when(sysUserCurrentRoleRepository.findByUserId(USER_ID)).thenReturn(Optional.of(currentRole(AREA_ID)));
|
||||
when(mainGroupResolver.resolve(USER_ID)).thenReturn(Optional.of(groupRel(GROUP_A_TEACHER, 1)));
|
||||
|
||||
assertEquals(Collections.singletonList(SCHOOL_A), compute().schoolGroupIds());
|
||||
}
|
||||
|
||||
/** 主组链上没有学校组(如挂在区域组下)→ 解析不出主校,保留历史宽口径而非降级为空 */
|
||||
@Test
|
||||
void computeScope_whenMainChainHasNoSchool_shouldKeepAllAdminSchools() {
|
||||
givenTwoSchoolAdmin();
|
||||
when(sysUserCurrentRoleRepository.findByUserId(USER_ID)).thenReturn(Optional.empty());
|
||||
when(mainGroupResolver.resolve(USER_ID)).thenReturn(Optional.of(groupRel(AREA_ID, 1)));
|
||||
|
||||
AccessScope scope = compute();
|
||||
|
||||
assertEquals(AccessLevelEnum.SCHOOL, scope.getLevel());
|
||||
assertTrue(scope.schoolGroupIds().containsAll(Arrays.asList(SCHOOL_A, SCHOOL_B)));
|
||||
}
|
||||
|
||||
/** 无任何组关系且无切换记录 → 同样保留历史宽口径(不误降为 NONE) */
|
||||
@Test
|
||||
void computeScope_whenNoGroupRel_shouldKeepAllAdminSchools() {
|
||||
givenTwoSchoolAdmin();
|
||||
when(sysUserCurrentRoleRepository.findByUserId(USER_ID)).thenReturn(Optional.empty());
|
||||
when(mainGroupResolver.resolve(USER_ID)).thenReturn(Optional.empty());
|
||||
|
||||
assertTrue(compute().schoolGroupIds().containsAll(Arrays.asList(SCHOOL_A, SCHOOL_B)));
|
||||
}
|
||||
|
||||
/** 主校不在其管理学校内(主组在别校)→ 不认主校,保留历史宽口径 */
|
||||
@Test
|
||||
void computeScope_whenMainSchoolNotAdministered_shouldKeepAllAdminSchools() {
|
||||
givenTwoSchoolAdmin();
|
||||
when(sysUserCurrentRoleRepository.findByUserId(USER_ID)).thenReturn(Optional.empty());
|
||||
// 主组挂在区域组下(非学校)→ resolveMainSchoolId 解析不出学校 → 宽口径
|
||||
when(mainGroupResolver.resolve(USER_ID)).thenReturn(Optional.of(groupRel(AREA_ID, 1)));
|
||||
|
||||
assertEquals(2, compute().schoolGroupIds().size());
|
||||
}
|
||||
|
||||
// ===================== 不受影响的分支 =====================
|
||||
|
||||
/** 超管:SUPER 且不收敛(不查身份行) */
|
||||
@Test
|
||||
void computeScope_whenSuperAdmin_shouldStaySuper() {
|
||||
SysUser superUser = new SysUser();
|
||||
superUser.setId(USER_ID);
|
||||
superUser.setType(UserTypeEnum.SUPER_ADMIN);
|
||||
when(sysUserRepository.findById(USER_ID)).thenReturn(Optional.of(superUser));
|
||||
|
||||
AccessScope scope = compute();
|
||||
|
||||
assertTrue(scope.isSuper());
|
||||
assertFalse(scope.isNone());
|
||||
}
|
||||
|
||||
/** 身份行不是管理员角色(如仅老师身份)→ 不进 SCHOOL 级 */
|
||||
@Test
|
||||
void computeScope_whenNoAdminIdentityRow_shouldNotBeSchoolLevel() {
|
||||
when(sysUserRepository.findById(USER_ID)).thenReturn(Optional.of(normalUser()));
|
||||
SysUserRoleRel teacher = new SysUserRoleRel();
|
||||
teacher.setUserId(USER_ID);
|
||||
teacher.setRoleId(SysRoleEnum.TEACHER.getKey());
|
||||
teacher.setSchoolId(SCHOOL_A);
|
||||
when(sysUserRoleRelRepository.findByUserIdAndStatus(USER_ID, StatusEnum.NORMAL))
|
||||
.thenReturn(Collections.singletonList(teacher));
|
||||
when(mainGroupResolver.resolve(anyInt())).thenReturn(Optional.empty());
|
||||
when(sysUserRoleRelRepository.findByUserIdInAndStatus(any(), any())).thenReturn(Collections.emptyList());
|
||||
|
||||
assertFalse(compute().getLevel() == AccessLevelEnum.SCHOOL);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user