Commit 128a0e3bd3847d11b7f2f9e169dd36fce3b0b581

Authored by YunaiV
1 parent dcdde362

fix(report): 补齐积木报表超级管理员权限

- 按当前积木2.5.4权限注解与默认实现补齐51项权限,统一为单个数组
- 保留超级管理员判断、普通用户权限及原有认证和签名校验
- 补充权限与角色的管理员、普通用户及未登录场景单测

参考 Gitee !1600:https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1600
yudao-module-report/src/main/java/cn/iocoder/yudao/module/report/framework/jmreport/core/service/JmReportTokenServiceImpl.java
... ... @@ -167,20 +167,65 @@ public class JmReportTokenServiceImpl implements JmReportTokenServiceI {
167 167 TenantContextHolder.setTenantId(loginUser.getTenantId());
168 168  
169 169 // 参见文档 https://help.jimureport.com/prodSafe/ 文档
170   - // 适配:如果是本系统的管理员,则返回积木报表(仪表盘/大屏设计器)的所有权限指令
  170 + // 适配:如果是本系统的管理员,则返回积木报表(仪表盘/大屏设计器、报表设计器)的所有权限指令
171 171 // 如果不处理,会碰到 https://t.zsxq.com/yzlkA 反馈的问题
  172 + // 权限清单参考 https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1600
172 173 Long userId = SecurityFrameworkUtils.getLoginUserId();
173 174 if (permissionApi.hasAnyRoles(userId, RoleCodeEnum.SUPER_ADMIN.getCode())) {
174 175 return new String[]{
175   - "drag:datasource:testConnection", // 数据库连接测试
176   - "drag:datasource:saveOrUpate", // 数据源保存
177   - "drag:datasource:delete", // 数据源删除
178   - "drag:analysis:sql", // SQL解析
179   - "drag:design:getTotalData", // 展示Online表单数据
180   - "drag:dataset:save", // 数据集保存
181   - "drag:dataset:delete", // 数据集删除
182   - "onl:drag:clear:recovery", // 清空回收站
183   - "onl:drag:page:delete" // 数据删除
  176 + // ========== 第一块:大屏(仪表盘 BI)按钮权限 ==========
  177 + "drag:datasource:testConnection", // 数据源连接测试
  178 + "drag:datasource:saveOrUpate", // 数据源新增/编辑
  179 + "drag:datasource:delete", // 数据源删除
  180 + "drag:datasource:deleteBatch", // 数据源批量删除
  181 + "drag:datasource:queryById", // 数据源详情查询
  182 + "drag:dataset:save", // 数据集保存
  183 + "drag:dataset:delete", // 数据集删除
  184 + "drag:analysis:sql", // SQL解析
  185 + "drag:design:getTotalData", // 仪表盘对Online表单展示数据
  186 + "drag:iconlib:config", // 图标库配置
  187 + "onl:drag:clear:recovery", // 清空回收站
  188 + "onl:drag:page:delete", // 仪表盘页面删除
  189 + "onl:drag:category:delete", // 仪表盘分类删除
  190 + "onl:drag:comp:add", // 组件新增
  191 + "onl:drag:comp:edit", // 组件编辑
  192 + "onl:drag:comp:delete", // 组件删除
  193 + "onl:drag:comp:deleteBatch", // 组件批量删除
  194 + "onl:drag:getRawTableData", // 获取原始表数据
  195 + // ========== 第二块:积木报表按钮权限 ==========
  196 + "jmreport:exportJob:plugin", // 导出任务-插件
  197 + "jmreport:exportJob:list", // 导出任务-列表
  198 + "jmreport:exportJob:save", // 导出任务-保存
  199 + "jmreport:exportJob:detail", // 导出任务-详情
  200 + "jmreport:exportJob:delete", // 导出任务-删除
  201 + "jmreport:exportJob:status", // 导出任务-状态变更
  202 + "jmreport:exportJob:run", // 导出任务-执行
  203 + "jmreport:exportJob:export", // 导出任务-导出
  204 + "jmreport:map:add", // 地图-新增
  205 + "jmreport:map:delete", // 地图-删除
  206 + "jmreport:reportGroup:list", // 报表分组-列表
  207 + "jmreport:reportGroup:detail", // 报表分组-详情
  208 + "jmreport:reportGroup:save", // 报表分组-保存
  209 + "jmreport:reportGroup:delete", // 报表分组-删除
  210 + "jmreport:category:add", // 报表分类-新增
  211 + "jmreport:category:edit", // 报表分类-编辑
  212 + "jmreport:category:delete", // 报表分类-删除
  213 + "jmreport:category:reduction", // 报表分类-还原
  214 + "jmreport:category:completelyDelete", // 报表分类-彻底删除
  215 + "jmreport:category:batchMove", // 报表分类-批量移动
  216 + "jmreport:category:folderSort", // 报表分类-文件夹排序
  217 + "jmreport:datasource:testConnection", // 数据源-连接测试
  218 + "jmreport:datasource:saveOrUpate", // 数据源-新增/编辑
  219 + "jmreport:datasource:delete", // 数据源-删除
  220 + "jmreport:datasource:query", // 数据源-查询(列表/详情)
  221 + "jmreport:design:loadTableData", // 设计器-加载表数据
  222 + "jmreport:design:parseField", // 设计器-解析字段(SQL/API/JavaBean/填报处理器)
  223 + "jmreport:design:loadTable", // 设计器-库表/字段元数据查询
  224 + "jmreport:design:saveDb", // 设计器-数据集保存
  225 + "jmreport:design:filesDataset", // 设计器-文件数据集
  226 + "jmreport:design:editEnhance", // 设计器-增强代码(JS/CSS/Python)
  227 + "jmreport:ai:tableMeta", // AI-表元数据管理
  228 + "jimu:chat2bi:permEdit" // Chat2BI-权限编辑
184 229 };
185 230 }
186 231 return null;
... ...
yudao-module-report/src/test/java/cn/iocoder/yudao/module/report/framework/jmreport/core/service/JmReportTokenServiceImplTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.report.framework.jmreport.core.service;
  2 +
  3 +import cn.hutool.core.util.ArrayUtil;
  4 +import cn.iocoder.yudao.framework.common.biz.system.oauth2.OAuth2TokenCommonApi;
  5 +import cn.iocoder.yudao.framework.common.biz.system.permission.PermissionCommonApi;
  6 +import cn.iocoder.yudao.framework.security.config.SecurityProperties;
  7 +import cn.iocoder.yudao.framework.security.core.LoginUser;
  8 +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
  9 +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
  10 +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
  11 +import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
  12 +import org.junit.jupiter.api.AfterEach;
  13 +import org.junit.jupiter.api.Test;
  14 +import org.mockito.InjectMocks;
  15 +import org.mockito.Mock;
  16 +import org.springframework.mock.web.MockHttpServletRequest;
  17 +import org.springframework.security.core.context.SecurityContextHolder;
  18 +
  19 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
  20 +import static org.junit.jupiter.api.Assertions.*;
  21 +import static org.mockito.Mockito.verifyNoInteractions;
  22 +import static org.mockito.Mockito.when;
  23 +
  24 +/**
  25 + * {@link JmReportTokenServiceImpl} 的单元测试类
  26 + *
  27 + * @author 芋道源码
  28 + */
  29 +public class JmReportTokenServiceImplTest extends BaseMockitoUnitTest {
  30 +
  31 + @InjectMocks
  32 + private JmReportTokenServiceImpl jmReportTokenService;
  33 +
  34 + @Mock
  35 + private OAuth2TokenCommonApi oauth2TokenApi;
  36 + @Mock
  37 + private PermissionCommonApi permissionApi;
  38 + @Mock
  39 + private SecurityProperties securityProperties;
  40 +
  41 + @AfterEach
  42 + public void tearDown() {
  43 + SecurityContextHolder.clearContext();
  44 + TenantContextHolder.clear();
  45 + }
  46 +
  47 + @Test
  48 + public void testGetPermissions_superAdmin() {
  49 + // 准备参数
  50 + Long userId = randomLongId();
  51 + Long tenantId = randomLongId();
  52 + SecurityFrameworkUtils.setLoginUser(new LoginUser().setId(userId).setTenantId(tenantId),
  53 + new MockHttpServletRequest());
  54 + // mock 方法
  55 + when(permissionApi.hasAnyRoles(userId, RoleCodeEnum.SUPER_ADMIN.getCode())).thenReturn(true);
  56 +
  57 + // 调用
  58 + String[] permissions = jmReportTokenService.getPermissions("token");
  59 + // 断言:原有的大屏权限仍保留
  60 + assertTrue(ArrayUtil.containsAll(permissions, "drag:datasource:testConnection", "drag:analysis:sql",
  61 + "onl:drag:page:delete"));
  62 + // 断言:大屏只校验权限、未配置时超级管理员会被拒绝的三处
  63 + assertTrue(ArrayUtil.containsAll(permissions, "drag:datasource:queryById", "drag:datasource:deleteBatch",
  64 + "onl:drag:getRawTableData"));
  65 + // 断言:报表设计器的扩展权限
  66 + assertTrue(ArrayUtil.containsAll(permissions, "jmreport:datasource:query", "jmreport:exportJob:export",
  67 + "jmreport:ai:tableMeta"));
  68 + assertEquals(permissions.length, ArrayUtil.distinct(permissions).length);
  69 + assertEquals(tenantId, TenantContextHolder.getTenantId());
  70 + }
  71 +
  72 + @Test
  73 + public void testGetPermissions_notSuperAdmin() {
  74 + // 准备参数
  75 + Long userId = randomLongId();
  76 + Long tenantId = randomLongId();
  77 + SecurityFrameworkUtils.setLoginUser(new LoginUser().setId(userId).setTenantId(tenantId),
  78 + new MockHttpServletRequest());
  79 + // mock 方法
  80 + when(permissionApi.hasAnyRoles(userId, RoleCodeEnum.SUPER_ADMIN.getCode())).thenReturn(false);
  81 +
  82 + // 调用
  83 + String[] permissions = jmReportTokenService.getPermissions("token");
  84 + // 断言
  85 + assertNull(permissions);
  86 + assertEquals(tenantId, TenantContextHolder.getTenantId());
  87 + }
  88 +
  89 + @Test
  90 + public void testGetPermissions_notLogin() {
  91 + // 调用
  92 + String[] permissions = jmReportTokenService.getPermissions("token");
  93 + // 断言
  94 + assertNull(permissions);
  95 + assertNull(TenantContextHolder.getTenantId());
  96 + verifyNoInteractions(permissionApi);
  97 + }
  98 +
  99 + @Test
  100 + public void testGetRoles_superAdmin() {
  101 + // 准备参数
  102 + Long userId = randomLongId();
  103 + Long tenantId = randomLongId();
  104 + SecurityFrameworkUtils.setLoginUser(new LoginUser().setId(userId).setTenantId(tenantId),
  105 + new MockHttpServletRequest());
  106 + // mock 方法
  107 + when(permissionApi.hasAnyRoles(userId, RoleCodeEnum.SUPER_ADMIN.getCode())).thenReturn(true);
  108 +
  109 + // 调用
  110 + String[] roles = jmReportTokenService.getRoles("token");
  111 + // 断言
  112 + assertArrayEquals(new String[]{"admin"}, roles);
  113 + assertEquals(tenantId, TenantContextHolder.getTenantId());
  114 + }
  115 +
  116 + @Test
  117 + public void testGetRoles_notSuperAdmin() {
  118 + // 准备参数
  119 + Long userId = randomLongId();
  120 + SecurityFrameworkUtils.setLoginUser(new LoginUser().setId(userId).setTenantId(randomLongId()),
  121 + new MockHttpServletRequest());
  122 + // mock 方法
  123 + when(permissionApi.hasAnyRoles(userId, RoleCodeEnum.SUPER_ADMIN.getCode())).thenReturn(false);
  124 +
  125 + // 调用
  126 + String[] roles = jmReportTokenService.getRoles("token");
  127 + // 断言
  128 + assertNull(roles);
  129 + }
  130 +
  131 + @Test
  132 + public void testGetRoles_notLogin() {
  133 + // 调用
  134 + String[] roles = jmReportTokenService.getRoles("token");
  135 + // 断言
  136 + assertNull(roles);
  137 + verifyNoInteractions(permissionApi);
  138 + }
  139 +
  140 +}
... ...