setUserPermsSelfEscalation_audit_test.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. package user
  2. import (
  3. "context"
  4. "errors"
  5. "math"
  6. "testing"
  7. "time"
  8. "perms-system-server/internal/consts"
  9. "perms-system-server/internal/loaders"
  10. "perms-system-server/internal/middleware"
  11. permModel "perms-system-server/internal/model/perm"
  12. productModel "perms-system-server/internal/model/product"
  13. "perms-system-server/internal/response"
  14. "perms-system-server/internal/svc"
  15. "perms-system-server/internal/testutil"
  16. "perms-system-server/internal/types"
  17. "github.com/stretchr/testify/assert"
  18. "github.com/stretchr/testify/require"
  19. )
  20. // TC-0743: H-A 修复回归 —— 普通 MEMBER 不得通过 SetUserPerms 给自己授予任何权限
  21. // (RequireProductAdminFor 前置校验必须拦截)。
  22. func TestSetUserPerms_MemberCannotSelfEscalate(t *testing.T) {
  23. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  24. conn := testutil.GetTestSqlConn()
  25. now := time.Now().Unix()
  26. bootstrap := context.Background()
  27. code := testutil.UniqueId()
  28. pRes, err := svcCtx.SysProductModel.Insert(bootstrap, &productModel.SysProduct{
  29. Code: code, Name: "p_" + code, AppKey: code + "_k", AppSecret: "s",
  30. Status: consts.StatusEnabled, CreateTime: now, UpdateTime: now,
  31. })
  32. require.NoError(t, err)
  33. pId, _ := pRes.LastInsertId()
  34. username := testutil.UniqueId()
  35. userId := insertTestUser(t, bootstrap, username, testutil.HashPassword("pw"))
  36. mId := insertTestMember(t, svcCtx, code, userId)
  37. permRes, err := svcCtx.SysPermModel.Insert(bootstrap, &permModel.SysPerm{
  38. ProductCode: code, Name: "escalate_p", Code: "esc_" + testutil.UniqueId(),
  39. Status: consts.StatusEnabled, CreateTime: now, UpdateTime: now,
  40. })
  41. require.NoError(t, err)
  42. permId, _ := permRes.LastInsertId()
  43. t.Cleanup(func() {
  44. testutil.CleanTableByField(bootstrap, conn, "`sys_user_perm`", "userId", userId)
  45. testutil.CleanTable(bootstrap, conn, "`sys_product_member`", mId)
  46. testutil.CleanTable(bootstrap, conn, "`sys_perm`", permId)
  47. testutil.CleanTable(bootstrap, conn, "`sys_user`", userId)
  48. testutil.CleanTable(bootstrap, conn, "`sys_product`", pId)
  49. })
  50. // caller = 目标用户本人,MemberType=MEMBER(非 ADMIN)
  51. callerCtx := middleware.WithUserDetails(context.Background(), &loaders.UserDetails{
  52. UserId: userId, Username: username,
  53. IsSuperAdmin: false,
  54. MemberType: consts.MemberTypeMember,
  55. Status: consts.StatusEnabled,
  56. ProductCode: code,
  57. DeptId: 1,
  58. DeptPath: "/1/",
  59. MinPermsLevel: math.MaxInt64,
  60. })
  61. err = NewSetUserPermsLogic(callerCtx, svcCtx).SetUserPerms(&types.SetPermsReq{
  62. UserId: userId, // 给自己
  63. Perms: []types.UserPermItem{{PermId: permId, Effect: consts.PermEffectAllow}},
  64. })
  65. require.Error(t, err, "MEMBER 不得自我授权")
  66. var ce *response.CodeError
  67. require.True(t, errors.As(err, &ce))
  68. assert.Equal(t, 403, ce.Code())
  69. assert.Contains(t, ce.Error(), "仅超级管理员或该产品的管理员可执行此操作")
  70. // 二次确认:没有任何 user_perm 记录被写入
  71. rows := findUserPerms(t, bootstrap, userId)
  72. assert.Len(t, rows, 0, "被拒绝的 SetUserPerms 不得在 DB 残留任何个性化权限")
  73. }
  74. // TC-0744: H-A 修复回归 —— DEVELOPER 调用者(非 ADMIN)同样被拦截,即便目标不是自己。
  75. func TestSetUserPerms_DeveloperCallerRejected(t *testing.T) {
  76. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  77. conn := testutil.GetTestSqlConn()
  78. bootstrap := context.Background()
  79. now := time.Now().Unix()
  80. code := testutil.UniqueId()
  81. pRes, err := svcCtx.SysProductModel.Insert(bootstrap, &productModel.SysProduct{
  82. Code: code, Name: "p_" + code, AppKey: code + "_k", AppSecret: "s",
  83. Status: consts.StatusEnabled, CreateTime: now, UpdateTime: now,
  84. })
  85. require.NoError(t, err)
  86. pId, _ := pRes.LastInsertId()
  87. targetUsername := "target_" + testutil.UniqueId()
  88. targetId := insertTestUser(t, bootstrap, targetUsername, testutil.HashPassword("pw"))
  89. mId := insertTestMember(t, svcCtx, code, targetId)
  90. t.Cleanup(func() {
  91. testutil.CleanTable(bootstrap, conn, "`sys_product_member`", mId)
  92. testutil.CleanTable(bootstrap, conn, "`sys_user`", targetId)
  93. testutil.CleanTable(bootstrap, conn, "`sys_product`", pId)
  94. })
  95. devCtx := middleware.WithUserDetails(context.Background(), &loaders.UserDetails{
  96. UserId: 777777, Username: "dev_caller",
  97. MemberType: consts.MemberTypeDeveloper, Status: consts.StatusEnabled,
  98. ProductCode: code, DeptId: 1, DeptPath: "/1/", MinPermsLevel: math.MaxInt64,
  99. })
  100. err = NewSetUserPermsLogic(devCtx, svcCtx).SetUserPerms(&types.SetPermsReq{
  101. UserId: targetId, Perms: []types.UserPermItem{},
  102. })
  103. require.Error(t, err)
  104. var ce *response.CodeError
  105. require.True(t, errors.As(err, &ce))
  106. assert.Equal(t, 403, ce.Code())
  107. assert.Contains(t, ce.Error(), "仅超级管理员或该产品的管理员可执行此操作")
  108. }
  109. // TC-0745: H-A 正向回归 —— 同产品 ADMIN 操作合法 MEMBER 目标(非自己)依旧放行。
  110. func TestSetUserPerms_ProductAdminStillWorks(t *testing.T) {
  111. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  112. conn := testutil.GetTestSqlConn()
  113. bootstrap := context.Background()
  114. now := time.Now().Unix()
  115. code := testutil.UniqueId()
  116. pRes, err := svcCtx.SysProductModel.Insert(bootstrap, &productModel.SysProduct{
  117. Code: code, Name: "p_" + code, AppKey: code + "_k", AppSecret: "s",
  118. Status: consts.StatusEnabled, CreateTime: now, UpdateTime: now,
  119. })
  120. require.NoError(t, err)
  121. pId, _ := pRes.LastInsertId()
  122. targetId := insertTestUser(t, bootstrap, "tgt_"+testutil.UniqueId(), testutil.HashPassword("pw"))
  123. mId := insertTestMember(t, svcCtx, code, targetId)
  124. permRes, err := svcCtx.SysPermModel.Insert(bootstrap, &permModel.SysPerm{
  125. ProductCode: code, Name: "ok_p", Code: "ok_" + testutil.UniqueId(),
  126. Status: consts.StatusEnabled, CreateTime: now, UpdateTime: now,
  127. })
  128. require.NoError(t, err)
  129. permId, _ := permRes.LastInsertId()
  130. t.Cleanup(func() {
  131. testutil.CleanTableByField(bootstrap, conn, "`sys_user_perm`", "userId", targetId)
  132. testutil.CleanTable(bootstrap, conn, "`sys_product_member`", mId)
  133. testutil.CleanTable(bootstrap, conn, "`sys_perm`", permId)
  134. testutil.CleanTable(bootstrap, conn, "`sys_user`", targetId)
  135. testutil.CleanTable(bootstrap, conn, "`sys_product`", pId)
  136. })
  137. adminCtx := middleware.WithUserDetails(context.Background(), &loaders.UserDetails{
  138. UserId: 999999, Username: "admin_caller",
  139. MemberType: consts.MemberTypeAdmin, Status: consts.StatusEnabled,
  140. ProductCode: code, DeptId: 1, DeptPath: "/1/", MinPermsLevel: math.MaxInt64,
  141. })
  142. err = NewSetUserPermsLogic(adminCtx, svcCtx).SetUserPerms(&types.SetPermsReq{
  143. UserId: targetId,
  144. Perms: []types.UserPermItem{{PermId: permId, Effect: consts.PermEffectAllow}},
  145. })
  146. require.NoError(t, err, "产品 ADMIN 正常路径必须放行")
  147. rows := findUserPerms(t, bootstrap, targetId)
  148. assert.Len(t, rows, 1, "ADMIN 授权后 DB 应有 1 条 user_perm")
  149. }