Files
Migu2.0/MiGu.Server/Controllers/OpsController.cs
T
zhaowei.huangandCursor 42978930ca feat: 迁入 MiGu.Server、平台前端与车辆列表 reflection 回退
从 Simple-FR 拆出 Platform.Server 并重命名为 MiGu.Server;frontends 源码与构建脚本迁入本仓库。地图监控在 projection/cars 失败或为空时回退 reflection 车辆列表;Simple 仓库已移除旧 Platform.Server。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 18:16:34 +08:00

65 lines
2.8 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.Collections.Concurrent;
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace MiGu.Server.Controllers;
/// <summary>
/// 运维白名单网关(占位)。真实落地时按架构 §5.1 + §10.5:
/// - 校验 scope=RCSMonitor 是否允许该 op
/// - YARP 转发到 SimpleLite /api/ops/*
/// - 写 OpsAuditLogsimple_main.db + 写 OpsLogsplatform.db)。
///
/// AR-4: 全 class 加 [Authorize] —— 至少要求登录,再按 op 白名单 + JWT ops claim 双校验。
/// </summary>
[ApiController]
[Authorize]
[Route("api/sl/ops")]
public class OpsController : ControllerBase
{
public record ExecuteRequest(string OpCode, string TargetId, string? Reason, string? IdempotencyKey);
public record ExecuteResponse(bool Ok, string AuditId, string? Message);
public record AuditEntry(string Id, DateTimeOffset Ts, string User, string Scope, string OpCode, string Target, string Result, string? Message);
private static readonly ConcurrentQueue<AuditEntry> Audits = new();
private static long _seq = 0;
private static readonly HashSet<string> Whitelist = new(StringComparer.Ordinal)
{
"ops.car.pause", "ops.car.resume", "ops.car.gohome", "ops.car.resetSession",
"ops.car.manualCharge", "ops.task.pause", "ops.task.cancel", "ops.task.reassign",
"ops.task.boostPriority", "monitor.note.write"
};
[HttpPost("execute")]
public ActionResult<ExecuteResponse> Execute([FromBody] ExecuteRequest req)
{
if (!Whitelist.Contains(req.OpCode))
return BadRequest(new { message = $"非白名单 op{req.OpCode}" });
// AR-4: JWT ops claim 二次校验 —— JWT 颁发时已写入用户被授权的 ops 列表(空格分隔),
// 这里要求 (op 在白名单) AND (op 在用户 ops claim)admin 的 "*" 会被特判通过。
var opsClaim = User.FindFirst("ops")?.Value ?? "";
var userOps = opsClaim.Split(' ', StringSplitOptions.RemoveEmptyEntries);
if (!userOps.Contains("*") && !userOps.Contains(req.OpCode))
return StatusCode(403, new { message = $"当前账号无权执行 {req.OpCode}" });
var user = User.Identity?.Name ?? User.FindFirstValue(ClaimTypes.NameIdentifier) ?? "anonymous";
var scope = User.FindFirst("scope")?.Value ?? "unknown";
var id = $"A{Interlocked.Increment(ref _seq):D6}";
var entry = new AuditEntry(id, DateTimeOffset.UtcNow, user, scope,
req.OpCode, req.TargetId, "ok", req.Reason);
Audits.Enqueue(entry);
while (Audits.Count > 200 && Audits.TryDequeue(out _)) { }
return Ok(new ExecuteResponse(true, id, null));
}
[HttpGet("audits")]
public IActionResult Audits200()
{
return Ok(Audits.Reverse());
}
}