AdoS0DimOpsAuthorizeAttribute.cs 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. using Admin.NET.Core;
  2. using Microsoft.AspNetCore.Http;
  3. using Microsoft.AspNetCore.Mvc;
  4. using Microsoft.AspNetCore.Mvc.Filters;
  5. using Microsoft.Extensions.DependencyInjection;
  6. namespace Admin.NET.Plugin.AiDOP.Infrastructure;
  7. /// <summary>
  8. /// S0 维度刷新(<c>/api/s0/dim/*</c>)的运维级授权门。
  9. ///
  10. /// <para><b>为什么必须显式加这道门</b>:平台的
  11. /// <c>Admin.NET.Web.Core/Handlers/JwtHandler.CheckAuthorizeAsync</c> 末行是
  12. /// <c>allBtnPermList.TrueForAll(u =&gt; !routeName.Equals(u))</c> ——
  13. /// <b>未登记到 <see cref="SysMenu"/>(Type=Btn) 的路由默认放行</b>。
  14. /// 实测 <c>aidopdev.SysMenu</c> 有 175 条按钮权限,匹配 <c>s0%</c> / <c>%dim%</c> 的为 <b>0</b>,
  15. /// 因此在加这道门之前,<b>任意已认证用户(含只读业务账号)都能触发
  16. /// 本租户 4 张 dim 表的 DELETE + 全量重建,且无频率限制</b>。
  17. /// 同一 fail-open 缺陷 S8 已于 2026-09-02 实测命中,见
  18. /// <see cref="S8.S8PermissionAttribute"/> 的说明段。</para>
  19. ///
  20. /// <para><b>为什么不复用 <see cref="S8.S8PermissionAttribute"/></b>:它依赖
  21. /// <c>IS8TenantRoleResolver</c> / <c>S8TrustedScopeResolver</c> / <c>S8PermissionCatalog</c>
  22. /// 三个 S8 专属组件。S0 直接引用会形成跨模块耦合,违反 CLAUDE.md 第九节
  23. /// 「禁止直接改/依赖对方目录的服务文件」。故本类只复用其**形状**,不复用其实现。</para>
  24. ///
  25. /// <para><b>本阶段口径(试点期,刻意从严)</b>:只放行超级管理员。
  26. /// 维度刷新目前是纯运维动作 —— 无前端入口、无消费方(Phase 1 明确 NO CONSUMER REPOINTING),
  27. /// 三个批次的全部实测也都是「超管登录时选定目标租户」这一条路径。
  28. /// 从严只可能**收窄**可达面,不可能引入新权限,是评审期修复的唯一可接受方向。</para>
  29. ///
  30. /// <para><b>发布前应替换为能力码门禁</b>:正式发布时应改为
  31. /// 「<c>SysMenu</c>(Type=Btn) 登记 <c>s0:dim:refresh</c> 等能力码 + 经 <c>SysRoleMenu</c> 授予运维角色」,
  32. /// 与 S8 同构。该项属发布准备范围,本批次不实施。</para>
  33. ///
  34. /// <para><b>失败语义</b>:不满足 → 直接 403,在进入 Action 与任何 Service 之前返回,DB 零写入。
  35. /// 取不到 <see cref="UserManager"/> 时同样按拒绝处理(fail-closed),
  36. /// 绝不 fail-open —— 本门禁存在的理由本身就是「本不该放行却放行了」。</para>
  37. /// </summary>
  38. [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
  39. public sealed class AdoS0DimOpsAuthorizeAttribute : Attribute, IAuthorizationFilter
  40. {
  41. public void OnAuthorization(AuthorizationFilterContext context)
  42. {
  43. // 认证本身由平台管道负责;这里只判「已认证身份是否够格做运维动作」。
  44. // 未认证时交回平台的 401 语义,不在此重复判定。
  45. var user = context.HttpContext.User;
  46. if (user?.Identity?.IsAuthenticated != true) return;
  47. var userManager = context.HttpContext.RequestServices.GetService<UserManager>();
  48. if (userManager is { SuperAdmin: true }) return;
  49. context.Result = new ObjectResult(new { message = "没有权限执行该操作" })
  50. {
  51. StatusCode = StatusCodes.Status403Forbidden
  52. };
  53. }
  54. }