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