using Admin.NET.Plugin.AiDOP.Const.S8; using Admin.NET.Plugin.AiDOP.Dto.S8; using Admin.NET.Plugin.AiDOP.Infrastructure; using Admin.NET.Plugin.AiDOP.Infrastructure.S8; using Admin.NET.Plugin.AiDOP.Service.S8; namespace Admin.NET.Plugin.AiDOP.Controllers.S8; /// /// S8 配置页:操作员(员工)↔ 系统账号绑定 / 可绑账号查询。 /// 入口收口在 /aidop/s8/config/roles,与 S8 配置中心同源。 /// /// S8-P0-3-MASTERDATA-TRUSTED-SCOPE-1:本控制器原先是 /// KNOWN-ISSUES I-001 里「6 个未接入可信作用域解析」之一,且是其中**唯一被实测利用**的越权面 /// —— 普通租户账号调 GET /config/operator-bindings(不带任何参数)可拿到 500 条 /// 其它租户员工的工号与真实姓名。现已接入 /// /// 入参语义factoryRefId 已从所有 Action 签名移除。客户端(含前端 /// withAidopTenantFactoryParams)仍可能继续携带该 query/body 字段,ASP.NET 会直接丢弃, /// **不产生任何授权效果**。Body 里的 AdoS8OperatorBindingCreateDto.FactoryRefId 同理不被读取。 /// [ApiController] [Route("api/aidop/s8/config")] [NonUnify] public class AdoS8ConfigBindingsController : ControllerBase { private readonly S8OperatorBindingService _svc; private readonly S8TrustedScopeResolver _scope; public AdoS8ConfigBindingsController(S8OperatorBindingService svc, S8TrustedScopeResolver scope) { _svc = svc; _scope = scope; } [HttpGet("operator-bindings")] [S8Permission(S8PermissionCatalog.ConfigRead)] public async Task ListAsync( [FromQuery] string? bindStatus = null, [FromQuery] string? keyword = null) { try { return Ok(await _svc.ListAsync(await _scope.ResolveAsync(), bindStatus, keyword)); } catch (S8BizException ex) { return BadRequest(new { message = ex.Message }); } } [HttpPost("operator-bindings")] [S8Permission(S8PermissionCatalog.ConfigOperatorBind)] public async Task BindAsync([FromBody] AdoS8OperatorBindingCreateDto body) { try { return Ok(await _svc.BindAsync(await _scope.ResolveAsync(), body)); } catch (S8BizException ex) { return BadRequest(new { message = ex.Message }); } } [HttpDelete("operator-bindings/{employeeId:long}")] [S8Permission(S8PermissionCatalog.ConfigOperatorBind)] public async Task UnbindAsync(long employeeId) { try { await _svc.UnbindAsync(await _scope.ResolveAsync(), employeeId); return Ok(new { employeeId }); } catch (S8BizException ex) { return BadRequest(new { message = ex.Message }); } } [HttpGet("sys-users")] [S8Permission(S8PermissionCatalog.ConfigRead)] public async Task SysUsersAsync( [FromQuery] string? keyword = null, [FromQuery] long? excludeEmployeeId = null) { try { return Ok(await _svc.ListSysUsersAsync(await _scope.ResolveAsync(), keyword, excludeEmployeeId)); } catch (S8BizException ex) { return BadRequest(new { message = ex.Message }); } } }