Просмотр исходного кода

fix(auth): UAT_FUNCTION_USER 补 sysOrg:list 权限,修复 S0 下拉 403

JwtHandler 将 /api/sysOrg/list 映射为权限名 sysOrg:list。该权限已登记于
SysMenu(Id=1300200030201),但 UAT_FUNCTION_USER 未授权,非超管账号命中
授权失败分支,返回 code=403「403 禁止访问,没有权限」(Return200StatusCodes
下 HTTP 仍为 200,故排查时易被误判为成功)。

S0 页面公司/工厂下拉统一走 loadOrgList(),被 27 个页面共用,每页并发请求
Type=201/501 两次,故每个 S0 页面弹两个 403,随后下拉为空并提示
「部分下拉数据加载失败」。该角色为 UATTEST_CHL / UATTEST_CHLB 两租户
共 11 个账号的公共角色,非单账号问题。

最小授权:只补 sysOrg:list 只读权限,不放开 sysOrg 写权限及其他平台管理权限。

chore: bump version server 1.0.386
YY968XX 14 часов назад
Родитель
Сommit
139d9788fc

+ 9 - 3
server/Admin.NET.Web.Entry/Admin.NET.Web.Entry.csproj

@@ -11,9 +11,9 @@
     <GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
     <Copyright>Admin.NET</Copyright>
     <Description>Admin.NET 通用权限开发平台</Description>
-    <AssemblyVersion>1.0.385</AssemblyVersion>
-    <FileVersion>1.0.385</FileVersion>
-    <Version>1.0.385</Version>
+    <AssemblyVersion>1.0.386</AssemblyVersion>
+    <FileVersion>1.0.386</FileVersion>
+    <Version>1.0.386</Version>
   </PropertyGroup>
 
   <ItemGroup>
@@ -490,6 +490,12 @@
     <None Update="UpdateScripts\1.0.384.verify.sql">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </None>
+    <None Update="UpdateScripts\1.0.386.sql">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </None>
+    <None Update="UpdateScripts\1.0.386.verify.sql">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </None>
     <None Update="UpdateScripts\1.0.364.verify.sql">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </None>

+ 52 - 0
server/Admin.NET.Web.Entry/UpdateScripts/1.0.386.sql

@@ -0,0 +1,52 @@
+-- 1.0.386: 给 UAT_FUNCTION_USER 角色补 sysOrg:list 按钮权限。
+--
+-- 背景:
+--   JwtHandler.CheckAuthorzieAsync 将 /api/sysOrg/list 映射为权限名 sysOrg:list。
+--   非 SuperAdmin 账号需命中「自有按钮权限」才放行;未命中时,只有该路由
+--   未登记为按钮权限才兜底放行。sysOrg:list 已登记(SysMenu Id=1300200030201),
+--   而 UAT_FUNCTION_USER 未授权 → 授权失败,接口返回 code=403
+--   「403 禁止访问,没有权限」(Furion Return200StatusCodes 下 HTTP 仍为 200)。
+--
+--   S0 页面的公司/工厂下拉统一走 loadOrgList()(Web/src/views/aidop/s0/api/s0SalesApi.ts),
+--   被 27 个 S0 页面共用,每页并发请求 Type=201(公司)与 Type=501(工厂)两次,
+--   故表现为每个 S0 页面弹两个 403,随后下拉为空并提示「部分下拉数据加载失败」。
+--   该角色为租户 UATTEST_CHL 全部 6 个账号的公共角色,影响面为该租户所有用户。
+--
+-- 最小授权:只补 sysOrg:list 这一条只读权限,不放开 sysOrg:add/update/delete,
+--   也不放开 sysCache / sysConfig / sysFile 等平台管理类权限。
+--
+-- 生效前提:GetOwnBtnPermList 结果缓存 7 天(CacheConst.KeyUserButton + userId),
+--   当前 CacheType=Memory,重启后端即失效重算。
+--
+-- 幂等:已授权则不重复插入;角色或菜单不存在的库上为空操作。
+
+DROP TEMPORARY TABLE IF EXISTS tmp_uat_org_list_rm;
+CREATE TEMPORARY TABLE tmp_uat_org_list_rm (
+  seq INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
+  role_id BIGINT NOT NULL,
+  menu_id BIGINT NOT NULL
+);
+
+INSERT INTO tmp_uat_org_list_rm (role_id, menu_id)
+SELECT r.Id, m.Id
+FROM SysRole r
+JOIN SysMenu m
+  ON m.Type = 3
+ AND m.Permission = 'sysOrg:list'
+ AND m.Status = 1
+WHERE r.Code = 'UAT_FUNCTION_USER'
+  AND r.Status = 1
+  AND EXISTS (
+    SELECT 1 FROM SysTenantMenu t WHERE t.TenantId = r.TenantId AND t.MenuId = m.Id
+  )
+  AND NOT EXISTS (
+    SELECT 1 FROM SysRoleMenu x WHERE x.RoleId = r.Id AND x.MenuId = m.Id
+  );
+
+SET @uat_org_list_rm_base := (SELECT IFNULL(MAX(Id), 0) FROM SysRoleMenu);
+
+INSERT INTO SysRoleMenu (Id, RoleId, MenuId)
+SELECT @uat_org_list_rm_base + s.seq, s.role_id, s.menu_id
+FROM tmp_uat_org_list_rm s;
+
+DROP TEMPORARY TABLE IF EXISTS tmp_uat_org_list_rm;

+ 31 - 0
server/Admin.NET.Web.Entry/UpdateScripts/1.0.386.verify.sql

@@ -0,0 +1,31 @@
+-- 1.0.386 verify: UAT_FUNCTION_USER 应已获得 sysOrg:list 按钮权限。
+--
+-- 约定:AutoVersionUpdate 取每条语句「首行首列」当布尔断言,必须为真。
+--   返回 0 行会得到 NULL 判假并中断启动,故断言一律返回标量真值。
+-- 环境容错:角色不存在的库上主脚本为空操作,断言同样短路为真。
+
+-- 1) 角色已绑定 sysOrg:list 菜单
+SELECT IF(
+  (SELECT COUNT(*) FROM SysRole WHERE Code = 'UAT_FUNCTION_USER' AND Status = 1) = 0,
+  1,
+  (SELECT COUNT(*)
+     FROM SysRoleMenu rm
+     JOIN SysRole r ON r.Id = rm.RoleId
+     JOIN SysMenu m ON m.Id = rm.MenuId
+    WHERE r.Code = 'UAT_FUNCTION_USER'
+      AND m.Permission = 'sysOrg:list'
+      AND m.Type = 3)
+) AS ok_binding;
+
+-- 2) 无重复绑定(幂等)
+SELECT IF(
+  (SELECT COUNT(*) FROM (
+     SELECT rm.RoleId, rm.MenuId
+       FROM SysRoleMenu rm
+       JOIN SysRole r ON r.Id = rm.RoleId
+      WHERE r.Code = 'UAT_FUNCTION_USER'
+      GROUP BY rm.RoleId, rm.MenuId
+     HAVING COUNT(*) > 1
+   ) dup) = 0,
+  1, 0
+) AS ok_no_duplicate;