rbac_handler.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. // RBAC 角色-权限点矩阵配置端点(T6.5-e,超管可配置后台,2026-11-06 用户拍板 #2)。
  2. //
  3. // 端点(平台级,不带多租户作用域;访问入口由路由角色白名单 RequireRole("super_admin") 限定):
  4. // - GET /org/role-permissions/:roleCode 查某角色当前矩阵 + 全量权限点字典(超管配置页渲染勾选树)
  5. // - PUT /org/role-permissions/:roleCode 全量保存某角色矩阵(删旧插新;保存后清本实例 RBAC 缓存)
  6. //
  7. // 安全模型(RBAC 三层第②层的"配置侧"):
  8. // - 路由挂 RequireRole("super_admin"):仅平台超管可读写(其他角色 1003;超管恒放行);
  9. // - 服务层校验 roleCode ∈ t_role 字典 + 权限码 ∈ t_permission 字典(防保存脏 code 污染矩阵);
  10. // - 保存成功后 Resolver.Invalidate() 清本实例缓存(跨实例靠 TTL 30s 自然一致)。
  11. //
  12. // 与同包 handler.Handler(机构/校区只读)的分工:本 handler 面向平台级 RBAC 字典
  13. // (t_role / t_permission / t_role_permission),**不走 scope**(无机构/校区过滤——
  14. // 全局配置与租户无关,越权防线完全由路由角色白名单承担)。
  15. package handler
  16. import (
  17. "encoding/json"
  18. "github.com/gofiber/fiber/v2"
  19. apperrors "yuansiwei/unified-backend/internal/infra/errors"
  20. "yuansiwei/unified-backend/internal/infra/response"
  21. "yuansiwei/unified-backend/internal/org/service"
  22. )
  23. // RBACHandler RBAC 矩阵配置 HTTP 处理器(启动装配一次,全进程共享)。
  24. //
  25. // 字段:
  26. // - svc:RBAC 矩阵配置服务(List/Save 编排,见 org/service.rbac_service.go)。
  27. type RBACHandler struct {
  28. svc *service.RolePermissionService
  29. }
  30. // RBACConfig RBAC handler 装配参数(T6.5-e main 按此构造)。
  31. type RBACConfig struct {
  32. // Svc RBAC 矩阵配置服务(必填,nil 时 NewRBAC 报错 fail-fast)。
  33. Svc *service.RolePermissionService
  34. }
  35. // NewRBAC 构造 RBAC 矩阵配置处理器(fail-fast:Svc 未注入不允许挂载端点)。
  36. //
  37. // 出参:(*RBACHandler, error)——Svc 为 nil 时 error(装配层 fail-fast)。
  38. func NewRBAC(cfg RBACConfig) (*RBACHandler, error) {
  39. if cfg.Svc == nil {
  40. return nil, apperrors.New(apperrors.CodeInternal, "org rbac handler: RolePermissionService 未注入")
  41. }
  42. return &RBACHandler{svc: cfg.Svc}, nil
  43. }
  44. // saveRolePermReq PUT /org/role-permissions/:roleCode 请求体。
  45. //
  46. // 字段:Permissions []string——目标全量权限点 code 集(可为空 = 清空该角色矩阵;
  47. // 每项须存在于 t_permission;空切片/缺失均合法)。
  48. type saveRolePermReq struct {
  49. Permissions []string `json:"permissions"`
  50. }
  51. // RolePermissions 查某角色的矩阵 + 全量权限点字典(GET /org/role-permissions/:roleCode,超管)。
  52. //
  53. // 出参(成功):code 200,data 为 service.RolePermissionView
  54. //
  55. // {roleCode, assigned[], dictionary[]}(assigned=该角色当前权限码;dictionary=全量 active 权限点,前端构树勾选)。
  56. //
  57. // 出参(失败):code 2001(roleCode 不在 t_role 字典)/ 1003(非超管,RequireRole 拦截)/ 5002(DB)。
  58. func (h *RBACHandler) RolePermissions(c *fiber.Ctx) error {
  59. roleCode := c.Params("roleCode")
  60. view, err := h.svc.ListRolePermissions(c.Context(), roleCode)
  61. if err != nil {
  62. return response.Fail(c, err)
  63. }
  64. return response.OK(c, view)
  65. }
  66. // SaveRolePermissions 全量保存某角色的权限点矩阵(PUT /org/role-permissions/:roleCode,超管)。
  67. //
  68. // 入参:请求体 {permissions: ["域.资源.动作", ...]}(可空 = 清空;每项须存在于 t_permission)。
  69. // 出参(成功):code 200,data 省略(操作成功无数据体);保存后清本实例 RBAC 缓存。
  70. // 出参(失败):code 2001(roleCode 非法 / 含未知权限码 / 请求体解析失败)/ 1003(非超管)/ 5002(DB 含事务回滚)。
  71. func (h *RBACHandler) SaveRolePermissions(c *fiber.Ctx) error {
  72. var req saveRolePermReq
  73. // 请求体可空(= 清空矩阵);非空但非 JSON 时校验失败 → 2002
  74. if len(c.Body()) > 0 {
  75. if err := json.Unmarshal(c.Body(), &req); err != nil {
  76. return response.Fail(c, apperrors.New(apperrors.CodeBadFormat, "请求体 JSON 解析失败"))
  77. }
  78. }
  79. roleCode := c.Params("roleCode")
  80. if err := h.svc.SaveRolePermissions(c.Context(), roleCode, req.Permissions); err != nil {
  81. return response.Fail(c, err)
  82. }
  83. return response.OK(c, nil)
  84. }