feat(rbac): RbacStore 持久化权限体系替代硬编码 UserStore
- 新增 PageCatalog / RbacModels / RbacStore / RbacController:用户、角色、页面/操作/控件授权落盘 data/rbac.json,支持运行时增删改并即时生效 - 密码改用 PBKDF2-SHA256(100k 迭代 + 16B 随机盐) 存储,校验走 FixedTimeEquals 防时序攻击;对外 DTO 绝不外泄盐/哈希 - AuthController 登录 / me / switch-scope 统一收敛到 BuildSession,按角色在当前 scope 的并集计算有效权限并签发 JWT - EffectivePermissions 增加 AllowedPages;移除旧的硬编码 UserStore - Program.cs 注册 RbacStore、新增 RbacAdmin 授权策略(ops claim 含 * 或 auth.manage),并按 SimpleLite:FollowParent 决定是否注册停机清理钩子
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
using MiGu.Server.Configs;
|
||||
|
||||
namespace MiGu.Server.Auth;
|
||||
|
||||
/// <summary>
|
||||
/// RBAC 角色。一个角色 = 一组「页面 + 操作码 + 控件可见性」授权,归属某个 scope。
|
||||
/// <list type="bullet">
|
||||
/// <item><see cref="Scope"/>:<c>Platform</c> / <c>RCSMonitor</c> / <c>*</c>(通用,对两个 scope 都生效)。</item>
|
||||
/// <item><see cref="Pages"/>:可访问页面 Key 集合(见 <see cref="PageCatalog"/>);含 <c>*</c> 表示该 scope 全部页面。</item>
|
||||
/// <item><see cref="Ops"/>:细粒度操作码(如 <c>ops.car.pause</c>);含 <c>*</c> 表示全部操作。</item>
|
||||
/// <item><see cref="WidgetGrants"/>:控件级可见性(hidden / readonly / interactive)。</item>
|
||||
/// <item><see cref="System"/>:内置系统角色,禁止删除(可改名/调权限但保底不被误删)。</item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class RbacRole
|
||||
{
|
||||
public string Id { get; set; } = "";
|
||||
public string Name { get; set; } = "";
|
||||
public string Description { get; set; } = "";
|
||||
public string Scope { get; set; } = PageCatalog.ScopePlatform;
|
||||
public List<string> Pages { get; set; } = new();
|
||||
public List<string> Ops { get; set; } = new();
|
||||
public List<WidgetGrantDto> WidgetGrants { get; set; } = new();
|
||||
public bool System { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RBAC 用户。密码以 PBKDF2-SHA256 哈希存储(<see cref="Salt"/> / <see cref="PasswordHash"/> 均为 base64)。
|
||||
/// 一个用户可拥有多个角色,其有效权限 = 当前 scope 下各角色授权的并集。
|
||||
/// </summary>
|
||||
public sealed class RbacUser
|
||||
{
|
||||
public string Id { get; set; } = "";
|
||||
public string Username { get; set; } = "";
|
||||
public string DisplayName { get; set; } = "";
|
||||
public bool Enabled { get; set; } = true;
|
||||
public List<string> RoleIds { get; set; } = new();
|
||||
public string Salt { get; set; } = "";
|
||||
public string PasswordHash { get; set; } = "";
|
||||
}
|
||||
|
||||
/// <summary>rbac.json 的根对象(内存 + 文件持久化)。</summary>
|
||||
public sealed class RbacSnapshot
|
||||
{
|
||||
public int Version { get; set; } = 1;
|
||||
public List<RbacRole> Roles { get; set; } = new();
|
||||
public List<RbacUser> Users { get; set; } = new();
|
||||
}
|
||||
|
||||
// ─────────────────────────── API DTO ───────────────────────────
|
||||
|
||||
/// <summary>对外用户视图:绝不含 Salt / PasswordHash。<see cref="Scopes"/> 为该用户可登录的 scope 集合。</summary>
|
||||
public sealed record RbacUserDto(
|
||||
string Id,
|
||||
string Username,
|
||||
string DisplayName,
|
||||
bool Enabled,
|
||||
List<string> RoleIds,
|
||||
List<string> Scopes);
|
||||
|
||||
public sealed record CreateUserRequest(
|
||||
string Username,
|
||||
string? DisplayName,
|
||||
string Password,
|
||||
List<string>? RoleIds,
|
||||
bool Enabled = true);
|
||||
|
||||
public sealed record UpdateUserRequest(
|
||||
string? DisplayName,
|
||||
List<string>? RoleIds,
|
||||
bool? Enabled);
|
||||
|
||||
public sealed record SetPasswordRequest(string Password);
|
||||
|
||||
public sealed record SaveRoleRequest(
|
||||
string? Id,
|
||||
string Name,
|
||||
string? Description,
|
||||
string Scope,
|
||||
List<string>? Pages,
|
||||
List<string>? Ops,
|
||||
List<WidgetGrantDto>? WidgetGrants);
|
||||
Reference in New Issue
Block a user