Files
Migu2.0/MiGu.Server/Controllers/RbacController.cs
T

138 lines
5.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MiGu.Server.Auth;
using MiGu.Server.Configs;
namespace MiGu.Server.Controllers;
/// <summary>
/// RBAC 管理端:用户 / 角色 / 权限页面分配。整个控制器要求 <c>RbacAdmin</c> 策略
/// JWT 的 ops claim 含 <c>*</c> 或 <c>auth.manage</c>),即只有「超级管理员」类账号可访问。
///
/// 对应前端「平台配置中心 → 权限与角色」页(/admin/config/auth)。
/// </summary>
[ApiController]
[Authorize(Policy = "RbacAdmin")]
[Route("api/rbac")]
public class RbacController : ControllerBase
{
public sealed record OpDef(string Code, string Label);
public sealed record WidgetDef(string Id, string Label);
/// <summary>可分配的操作码候选(管理端配置角色时下拉/勾选用)。</summary>
private static readonly OpDef[] KnownOps =
{
new("*", "全部操作(通配)"),
new("ops.car.pause", "车辆 · 暂停"),
new("ops.car.resume", "车辆 · 恢复"),
new("ops.car.gohome", "车辆 · 回库"),
new("ops.car.resetSession", "车辆 · 重置会话"),
new("ops.car.manualCharge", "车辆 · 手动充电"),
new("ops.task.pause", "任务 · 暂停"),
new("ops.task.cancel", "任务 · 取消"),
new("ops.task.reassign", "任务 · 改派"),
new("ops.task.boostPriority", "任务 · 提升优先级"),
new("monitor.note.write", "监控 · 写运营备注"),
new("auth.manage", "系统 · 权限与角色管理"),
};
/// <summary>可配置可见性的控件候选。</summary>
private static readonly WidgetDef[] KnownWidgets =
{
new("MapEditor", "地图编辑器"),
new("CadToolbar", "CAD 工具栏"),
new("CarPanel", "车辆面板"),
new("MissionEditor", "任务编辑器"),
new("OpsActionPanel", "运维操作面板"),
new("ConfigCenter", "配置中心"),
};
private readonly RbacStore _store;
private readonly ILogger<RbacController> _log;
public RbacController(RbacStore store, ILogger<RbacController> log)
{
_store = store;
_log = log;
}
/// <summary>权限「字典」:页面清单 + 可选操作码 + 可选控件 + scope 选项。前端角色编辑器据此渲染勾选项。</summary>
[HttpGet("catalog")]
public IActionResult Catalog() => Ok(new
{
pages = PageCatalog.All,
ops = KnownOps,
widgets = KnownWidgets,
scopes = new[]
{
new { value = PageCatalog.ScopePlatform, label = "管理端 (Platform)" },
new { value = PageCatalog.ScopeMonitor, label = "运营端 (RCSMonitor)" },
new { value = PageCatalog.Wildcard, label = "通用 (全部域)" },
}
});
// ───────────────────────── 角色 ─────────────────────────
[HttpGet("roles")]
public IActionResult ListRoles() => Ok(_store.ListRoles());
[HttpPost("roles")]
public IActionResult CreateRole([FromBody] SaveRoleRequest req) => Guard(() => Ok(_store.CreateRole(req)));
[HttpPut("roles/{id}")]
public IActionResult UpdateRole(string id, [FromBody] SaveRoleRequest req) => Guard(() => Ok(_store.UpdateRole(id, req)));
[HttpDelete("roles/{id}")]
public IActionResult DeleteRole(string id) => Guard(() =>
{
_store.DeleteRole(id);
return Ok(new { ok = true });
});
// ───────────────────────── 用户 ─────────────────────────
[HttpGet("users")]
public IActionResult ListUsers() => Ok(_store.ListUsers());
[HttpPost("users")]
public IActionResult CreateUser([FromBody] CreateUserRequest req) => Guard(() => Ok(_store.CreateUser(req)));
[HttpPut("users/{id}")]
public IActionResult UpdateUser(string id, [FromBody] UpdateUserRequest req) => Guard(() =>
{
// 自我保护:禁止把当前登录账号自己停用,避免管理员把自己锁在门外。
if (id == CurrentUserId() && req.Enabled == false)
return (IActionResult)BadRequest(new { message = "不能停用当前登录的账号" });
return Ok(_store.UpdateUser(id, req));
});
[HttpPut("users/{id}/password")]
public IActionResult SetPassword(string id, [FromBody] SetPasswordRequest req) => Guard(() =>
{
_store.SetPassword(id, req.Password);
return Ok(new { ok = true });
});
[HttpDelete("users/{id}")]
public IActionResult DeleteUser(string id) => Guard(() =>
{
if (id == CurrentUserId())
return (IActionResult)BadRequest(new { message = "不能删除当前登录的账号" });
_store.DeleteUser(id);
return Ok(new { ok = true });
});
// ───────────────────────── 工具 ─────────────────────────
/// <summary>统一把 <see cref="RbacException"/> 翻译成 400 + message,其余异常向上抛。</summary>
private IActionResult Guard(Func<IActionResult> action)
{
try { return action(); }
catch (RbacException ex) { return BadRequest(new { message = ex.Message }); }
}
private string? CurrentUserId() =>
User.FindFirstValue("sub") ?? User.FindFirstValue(ClaimTypes.NameIdentifier);
}