bindRolesEqualLevel_audit_test.go 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. package user
  2. import (
  3. "errors"
  4. "testing"
  5. "perms-system-server/internal/consts"
  6. "perms-system-server/internal/loaders"
  7. userModel "perms-system-server/internal/model/user"
  8. "perms-system-server/internal/response"
  9. "perms-system-server/internal/svc"
  10. "perms-system-server/internal/testutil"
  11. "perms-system-server/internal/testutil/ctxhelper"
  12. "perms-system-server/internal/types"
  13. "github.com/stretchr/testify/assert"
  14. "github.com/stretchr/testify/require"
  15. )
  16. // ---------------------------------------------------------------------------
  17. // 覆盖目标:审计 H-3 修复 —— "不能分配与自己同级(或更高)的角色"。
  18. // 修复前代码仅拦 `>` 严格高于,允许 MEMBER 调用者把同级角色分配给别人,继而下一次 BindRoles 时
  19. // 由于同级权限集相同,可用后续 upgrade 路径放大;修复后变为 `<=`(含同级)拦截。
  20. // 本文件作为"同级也必须 403"的契约锚点。
  21. // ---------------------------------------------------------------------------
  22. // TC-0813: H-3 —— MEMBER 调用者不能分配与自己同 permsLevel 的角色。
  23. func TestBindRoles_EqualPermsLevel_Rejected(t *testing.T) {
  24. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  25. conn := testutil.GetTestSqlConn()
  26. superCtx := ctxhelper.SuperAdminCtx()
  27. deptId, deptPath, cleanupDept := setupDeptForCaller(t, svcCtx)
  28. t.Cleanup(cleanupDept)
  29. productCode := "test_product"
  30. username := testutil.UniqueId()
  31. targetUserId := insertTestUserFull(t, superCtx, &userModel.SysUser{
  32. Username: username, Password: testutil.HashPassword("pass"),
  33. Nickname: "tgt_eq", DeptId: deptId,
  34. IsSuperAdmin: consts.IsSuperAdminNo, MustChangePassword: 2, Status: consts.StatusEnabled,
  35. })
  36. mId := insertTestMember(t, svcCtx, productCode, targetUserId)
  37. const callerLevel int64 = 50
  38. sameLevelRole := insertTestRoleWithLevel(t, svcCtx, productCode, consts.StatusEnabled, callerLevel)
  39. t.Cleanup(func() {
  40. testutil.CleanTableByField(superCtx, conn, "`sys_user_role`", "userId", targetUserId)
  41. testutil.CleanTable(superCtx, conn, "`sys_product_member`", mId)
  42. testutil.CleanTable(superCtx, conn, "`sys_user`", targetUserId)
  43. testutil.CleanTable(superCtx, conn, "`sys_role`", sameLevelRole)
  44. })
  45. ctx := ctxhelper.CustomCtx(&loaders.UserDetails{
  46. UserId: 999994,
  47. Username: "member_eq_level",
  48. IsSuperAdmin: false,
  49. MemberType: consts.MemberTypeMember,
  50. Status: consts.StatusEnabled,
  51. ProductCode: productCode,
  52. DeptId: deptId,
  53. DeptPath: deptPath,
  54. MinPermsLevel: callerLevel,
  55. })
  56. err := NewBindRolesLogic(ctx, svcCtx).BindRoles(&types.BindRolesReq{
  57. UserId: targetUserId,
  58. RoleIds: []int64{sameLevelRole},
  59. })
  60. require.Error(t, err, "H-3 防线:同级角色分配必须被拒绝(含同级)")
  61. var ce *response.CodeError
  62. require.True(t, errors.As(err, &ce))
  63. assert.Equal(t, 403, ce.Code())
  64. assert.Contains(t, ce.Error(), "不能分配权限级别高于自身的角色",
  65. "错误消息应当明确点出'含同级'的拦截语义")
  66. // 同时验证 DB 未产生任何 user-role 关系。
  67. rids, err := svcCtx.SysUserRoleModel.FindRoleIdsByUserIdForProduct(ctx, targetUserId, productCode)
  68. require.NoError(t, err)
  69. assert.Empty(t, rids, "被拒绝的 BindRoles 不得落地任何行")
  70. }