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