bindRolesLogic_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. package user
  2. import (
  3. "errors"
  4. "testing"
  5. "time"
  6. "perms-system-server/internal/consts"
  7. "perms-system-server/internal/loaders"
  8. memberModel "perms-system-server/internal/model/productmember"
  9. roleModel "perms-system-server/internal/model/role"
  10. "perms-system-server/internal/response"
  11. "perms-system-server/internal/svc"
  12. "perms-system-server/internal/testutil"
  13. "perms-system-server/internal/testutil/ctxhelper"
  14. "perms-system-server/internal/types"
  15. "github.com/stretchr/testify/assert"
  16. "github.com/stretchr/testify/require"
  17. )
  18. func insertTestMember(t *testing.T, svcCtx *svc.ServiceContext, productCode string, userId int64) int64 {
  19. t.Helper()
  20. now := time.Now().Unix()
  21. res, err := svcCtx.SysProductMemberModel.Insert(ctxhelper.SuperAdminCtx(), &memberModel.SysProductMember{
  22. ProductCode: productCode,
  23. UserId: userId,
  24. MemberType: "MEMBER",
  25. Status: 1,
  26. CreateTime: now,
  27. UpdateTime: now,
  28. })
  29. require.NoError(t, err)
  30. id, _ := res.LastInsertId()
  31. return id
  32. }
  33. func insertTestRole(t *testing.T, svcCtx *svc.ServiceContext, productCode string, status int64) int64 {
  34. t.Helper()
  35. now := time.Now().Unix()
  36. res, err := svcCtx.SysRoleModel.Insert(ctxhelper.SuperAdminCtx(), &roleModel.SysRole{
  37. ProductCode: productCode,
  38. Name: "role_" + testutil.UniqueId(),
  39. Status: status,
  40. PermsLevel: 1,
  41. CreateTime: now,
  42. UpdateTime: now,
  43. })
  44. require.NoError(t, err)
  45. id, _ := res.LastInsertId()
  46. return id
  47. }
  48. // TC-0184: 正常绑定
  49. func TestBindRoles_Success(t *testing.T) {
  50. ctx := ctxhelper.SuperAdminCtx()
  51. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  52. conn := testutil.GetTestSqlConn()
  53. username := testutil.UniqueId()
  54. userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass"))
  55. mId := insertTestMember(t, svcCtx, "test_product", userId)
  56. r1 := insertTestRole(t, svcCtx, "test_product", 1)
  57. r2 := insertTestRole(t, svcCtx, "test_product", 1)
  58. t.Cleanup(func() {
  59. testutil.CleanTableByField(ctx, conn, "`sys_user_role`", "userId", userId)
  60. testutil.CleanTable(ctx, conn, "`sys_product_member`", mId)
  61. testutil.CleanTable(ctx, conn, "`sys_user`", userId)
  62. testutil.CleanTable(ctx, conn, "`sys_role`", r1, r2)
  63. })
  64. logic := NewBindRolesLogic(ctx, svcCtx)
  65. err := logic.BindRoles(&types.BindRolesReq{
  66. UserId: userId,
  67. RoleIds: []int64{r1, r2},
  68. })
  69. require.NoError(t, err)
  70. roleIds, err := svcCtx.SysUserRoleModel.FindRoleIdsByUserId(ctx, userId)
  71. require.NoError(t, err)
  72. assert.ElementsMatch(t, []int64{r1, r2}, roleIds)
  73. }
  74. // TC-0185: 用户不存在
  75. func TestBindRoles_UserNotFound(t *testing.T) {
  76. ctx := ctxhelper.SuperAdminCtx()
  77. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  78. logic := NewBindRolesLogic(ctx, svcCtx)
  79. err := logic.BindRoles(&types.BindRolesReq{
  80. UserId: 999999999,
  81. RoleIds: []int64{1},
  82. })
  83. require.Error(t, err)
  84. var codeErr *response.CodeError
  85. require.True(t, errors.As(err, &codeErr))
  86. assert.Equal(t, 404, codeErr.Code())
  87. assert.Equal(t, "用户不存在", codeErr.Error())
  88. }
  89. // TC-0186: 清空角色
  90. func TestBindRoles_EmptyRoleIds_ClearsAll(t *testing.T) {
  91. ctx := ctxhelper.SuperAdminCtx()
  92. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  93. conn := testutil.GetTestSqlConn()
  94. username := testutil.UniqueId()
  95. userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass"))
  96. mId := insertTestMember(t, svcCtx, "test_product", userId)
  97. r1 := insertTestRole(t, svcCtx, "test_product", 1)
  98. t.Cleanup(func() {
  99. testutil.CleanTableByField(ctx, conn, "`sys_user_role`", "userId", userId)
  100. testutil.CleanTable(ctx, conn, "`sys_product_member`", mId)
  101. testutil.CleanTable(ctx, conn, "`sys_user`", userId)
  102. testutil.CleanTable(ctx, conn, "`sys_role`", r1)
  103. })
  104. logic := NewBindRolesLogic(ctx, svcCtx)
  105. err := logic.BindRoles(&types.BindRolesReq{
  106. UserId: userId,
  107. RoleIds: []int64{r1},
  108. })
  109. require.NoError(t, err)
  110. err = logic.BindRoles(&types.BindRolesReq{
  111. UserId: userId,
  112. RoleIds: []int64{},
  113. })
  114. require.NoError(t, err)
  115. roleIds, err := svcCtx.SysUserRoleModel.FindRoleIdsByUserId(ctx, userId)
  116. require.NoError(t, err)
  117. assert.Empty(t, roleIds)
  118. }
  119. // TC-0184: 正常重新绑定
  120. func TestBindRoles_Rebind(t *testing.T) {
  121. ctx := ctxhelper.SuperAdminCtx()
  122. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  123. conn := testutil.GetTestSqlConn()
  124. username := testutil.UniqueId()
  125. userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass"))
  126. mId := insertTestMember(t, svcCtx, "test_product", userId)
  127. r1 := insertTestRole(t, svcCtx, "test_product", 1)
  128. r2 := insertTestRole(t, svcCtx, "test_product", 1)
  129. r3 := insertTestRole(t, svcCtx, "test_product", 1)
  130. t.Cleanup(func() {
  131. testutil.CleanTableByField(ctx, conn, "`sys_user_role`", "userId", userId)
  132. testutil.CleanTable(ctx, conn, "`sys_product_member`", mId)
  133. testutil.CleanTable(ctx, conn, "`sys_user`", userId)
  134. testutil.CleanTable(ctx, conn, "`sys_role`", r1, r2, r3)
  135. })
  136. logic := NewBindRolesLogic(ctx, svcCtx)
  137. err := logic.BindRoles(&types.BindRolesReq{
  138. UserId: userId,
  139. RoleIds: []int64{r1, r2},
  140. })
  141. require.NoError(t, err)
  142. err = logic.BindRoles(&types.BindRolesReq{
  143. UserId: userId,
  144. RoleIds: []int64{r2, r3},
  145. })
  146. require.NoError(t, err)
  147. roleIds, err := svcCtx.SysUserRoleModel.FindRoleIdsByUserId(ctx, userId)
  148. require.NoError(t, err)
  149. assert.ElementsMatch(t, []int64{r2, r3}, roleIds)
  150. }
  151. // TC-0188: 角色不属于当前产品
  152. func TestBindRoles_RoleBelongsToOtherProduct(t *testing.T) {
  153. ctx := ctxhelper.SuperAdminCtx()
  154. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  155. conn := testutil.GetTestSqlConn()
  156. username := testutil.UniqueId()
  157. userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass"))
  158. mId := insertTestMember(t, svcCtx, "test_product", userId)
  159. otherRole := insertTestRole(t, svcCtx, "other_product", 1)
  160. t.Cleanup(func() {
  161. testutil.CleanTable(ctx, conn, "`sys_product_member`", mId)
  162. testutil.CleanTable(ctx, conn, "`sys_user`", userId)
  163. testutil.CleanTable(ctx, conn, "`sys_role`", otherRole)
  164. })
  165. logic := NewBindRolesLogic(ctx, svcCtx)
  166. err := logic.BindRoles(&types.BindRolesReq{
  167. UserId: userId,
  168. RoleIds: []int64{otherRole},
  169. })
  170. require.Error(t, err)
  171. var codeErr *response.CodeError
  172. require.True(t, errors.As(err, &codeErr))
  173. assert.Equal(t, 400, codeErr.Code())
  174. assert.Contains(t, codeErr.Error(), "其他产品的角色")
  175. }
  176. // TC-0189: 角色已禁用
  177. func TestBindRoles_RoleDisabled(t *testing.T) {
  178. ctx := ctxhelper.SuperAdminCtx()
  179. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  180. conn := testutil.GetTestSqlConn()
  181. username := testutil.UniqueId()
  182. userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass"))
  183. mId := insertTestMember(t, svcCtx, "test_product", userId)
  184. disabledRole := insertTestRole(t, svcCtx, "test_product", 2)
  185. t.Cleanup(func() {
  186. testutil.CleanTable(ctx, conn, "`sys_product_member`", mId)
  187. testutil.CleanTable(ctx, conn, "`sys_user`", userId)
  188. testutil.CleanTable(ctx, conn, "`sys_role`", disabledRole)
  189. })
  190. logic := NewBindRolesLogic(ctx, svcCtx)
  191. err := logic.BindRoles(&types.BindRolesReq{
  192. UserId: userId,
  193. RoleIds: []int64{disabledRole},
  194. })
  195. require.Error(t, err)
  196. var codeErr *response.CodeError
  197. require.True(t, errors.As(err, &codeErr))
  198. assert.Equal(t, 400, codeErr.Code())
  199. assert.Contains(t, codeErr.Error(), "已禁用的角色")
  200. }
  201. // TC-0190: 角色不存在
  202. func TestBindRoles_RoleNotExists(t *testing.T) {
  203. ctx := ctxhelper.SuperAdminCtx()
  204. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  205. conn := testutil.GetTestSqlConn()
  206. username := testutil.UniqueId()
  207. userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass"))
  208. mId := insertTestMember(t, svcCtx, "test_product", userId)
  209. t.Cleanup(func() {
  210. testutil.CleanTable(ctx, conn, "`sys_product_member`", mId)
  211. testutil.CleanTable(ctx, conn, "`sys_user`", userId)
  212. })
  213. logic := NewBindRolesLogic(ctx, svcCtx)
  214. err := logic.BindRoles(&types.BindRolesReq{
  215. UserId: userId,
  216. RoleIds: []int64{999999999},
  217. })
  218. require.Error(t, err)
  219. var codeErr *response.CodeError
  220. require.True(t, errors.As(err, &codeErr))
  221. assert.Equal(t, 400, codeErr.Code())
  222. assert.Contains(t, codeErr.Error(), "无效的角色ID")
  223. }
  224. func insertTestRoleWithLevel(t *testing.T, svcCtx *svc.ServiceContext, productCode string, status int64, permsLevel int64) int64 {
  225. t.Helper()
  226. now := time.Now().Unix()
  227. res, err := svcCtx.SysRoleModel.Insert(ctxhelper.SuperAdminCtx(), &roleModel.SysRole{
  228. ProductCode: productCode,
  229. Name: "role_" + testutil.UniqueId(),
  230. Status: status,
  231. PermsLevel: permsLevel,
  232. CreateTime: now,
  233. UpdateTime: now,
  234. })
  235. require.NoError(t, err)
  236. id, _ := res.LastInsertId()
  237. return id
  238. }
  239. // TC-0208: 非超管不能分配权限级别高于自身的角色(审计#2修复验证)
  240. func TestBindRoles_PermsLevelEscalation_Rejected(t *testing.T) {
  241. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  242. conn := testutil.GetTestSqlConn()
  243. superCtx := ctxhelper.SuperAdminCtx()
  244. productCode := "test_product"
  245. username := testutil.UniqueId()
  246. userId := insertTestUser(t, superCtx, username, testutil.HashPassword("pass"))
  247. mId := insertTestMember(t, svcCtx, productCode, userId)
  248. highLevelRole := insertTestRoleWithLevel(t, svcCtx, productCode, 1, 1)
  249. t.Cleanup(func() {
  250. testutil.CleanTableByField(superCtx, conn, "`sys_user_role`", "userId", userId)
  251. testutil.CleanTable(superCtx, conn, "`sys_product_member`", mId)
  252. testutil.CleanTable(superCtx, conn, "`sys_user`", userId)
  253. testutil.CleanTable(superCtx, conn, "`sys_role`", highLevelRole)
  254. })
  255. ctx := ctxhelper.CustomCtx(&loaders.UserDetails{
  256. UserId: 999998,
  257. Username: "admin_caller",
  258. IsSuperAdmin: false,
  259. MemberType: consts.MemberTypeAdmin,
  260. Status: consts.StatusEnabled,
  261. ProductCode: productCode,
  262. DeptId: 1,
  263. DeptPath: "/1/",
  264. MinPermsLevel: 50,
  265. })
  266. logic := NewBindRolesLogic(ctx, svcCtx)
  267. err := logic.BindRoles(&types.BindRolesReq{
  268. UserId: userId,
  269. RoleIds: []int64{highLevelRole},
  270. })
  271. require.Error(t, err)
  272. var ce *response.CodeError
  273. require.True(t, errors.As(err, &ce))
  274. assert.Equal(t, 403, ce.Code())
  275. assert.Contains(t, ce.Error(), "不能分配权限级别高于自身的角色")
  276. }
  277. // TC-0209: 超管可以分配任意权限级别的角色
  278. func TestBindRoles_SuperAdminCanAssignAnyLevel(t *testing.T) {
  279. ctx := ctxhelper.SuperAdminCtx()
  280. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  281. conn := testutil.GetTestSqlConn()
  282. productCode := "test_product"
  283. username := testutil.UniqueId()
  284. userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass"))
  285. mId := insertTestMember(t, svcCtx, productCode, userId)
  286. highLevelRole := insertTestRoleWithLevel(t, svcCtx, productCode, 1, 1)
  287. t.Cleanup(func() {
  288. testutil.CleanTableByField(ctx, conn, "`sys_user_role`", "userId", userId)
  289. testutil.CleanTable(ctx, conn, "`sys_product_member`", mId)
  290. testutil.CleanTable(ctx, conn, "`sys_user`", userId)
  291. testutil.CleanTable(ctx, conn, "`sys_role`", highLevelRole)
  292. })
  293. logic := NewBindRolesLogic(ctx, svcCtx)
  294. err := logic.BindRoles(&types.BindRolesReq{
  295. UserId: userId,
  296. RoleIds: []int64{highLevelRole},
  297. })
  298. require.NoError(t, err)
  299. roleIds, err := svcCtx.SysUserRoleModel.FindRoleIdsByUserId(ctx, userId)
  300. require.NoError(t, err)
  301. assert.Contains(t, roleIds, highLevelRole)
  302. }
  303. // TC-0191: 目标用户不是当前产品成员时拒绝绑定角色(L-4修复验证)
  304. func TestBindRoles_NonMemberRejected(t *testing.T) {
  305. ctx := ctxhelper.SuperAdminCtx()
  306. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  307. conn := testutil.GetTestSqlConn()
  308. username := testutil.UniqueId()
  309. userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass"))
  310. t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) })
  311. logic := NewBindRolesLogic(ctx, svcCtx)
  312. err := logic.BindRoles(&types.BindRolesReq{
  313. UserId: userId,
  314. RoleIds: []int64{},
  315. })
  316. require.Error(t, err)
  317. var codeErr2 *response.CodeError
  318. require.True(t, errors.As(err, &codeErr2))
  319. assert.Equal(t, 400, codeErr2.Code())
  320. assert.Contains(t, codeErr2.Error(), "不是当前产品的成员")
  321. }