package user import ( "errors" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "perms-system-server/internal/consts" "perms-system-server/internal/loaders" memberModel "perms-system-server/internal/model/productmember" "perms-system-server/internal/response" "perms-system-server/internal/svc" "perms-system-server/internal/testutil" "perms-system-server/internal/testutil/ctxhelper" "perms-system-server/internal/types" ) // insertTestMemberWithType 创建指定类型的产品成员,供 getUserPerms 测试使用。 func insertTestMemberWithType(t *testing.T, svcCtx *svc.ServiceContext, productCode string, userId int64, memberType string) int64 { t.Helper() now := time.Now().Unix() res, err := svcCtx.SysProductMemberModel.Insert(ctxhelper.SuperAdminCtx(), &memberModel.SysProductMember{ ProductCode: productCode, UserId: userId, MemberType: memberType, Status: consts.StatusEnabled, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) id, _ := res.LastInsertId() return id } // insertUserPerm 直接向 sys_user_perm 插入一条覆盖记录,绕过 SetUserPerms 的业务校验。 func insertUserPerm(t *testing.T, svcCtx *svc.ServiceContext, userId, permId int64, effect string) { t.Helper() conn := testutil.GetTestSqlConn() now := time.Now().Unix() _, err := conn.ExecCtx(ctxhelper.SuperAdminCtx(), "INSERT INTO `sys_user_perm` (`userId`,`permId`,`effect`,`createTime`,`updateTime`) VALUES (?,?,?,?,?)", userId, permId, effect, now, now) require.NoError(t, err) } // TC-1257: 超管查任意用户权限覆盖 func TestGetUserPerms_SuperAdmin(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() username := testutil.UniqueId() userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass")) mId := insertTestMember(t, svcCtx, "test_product", userId) permId := insertTestPerm(t, svcCtx, "test_product") insertUserPerm(t, svcCtx, userId, permId, consts.PermEffectAllow) t.Cleanup(func() { testutil.CleanTableByField(ctx, conn, "`sys_user_perm`", "userId", userId) testutil.CleanTable(ctx, conn, "`sys_product_member`", mId) testutil.CleanTable(ctx, conn, "`sys_user`", userId) testutil.CleanTable(ctx, conn, "`sys_perm`", permId) }) resp, err := NewGetUserPermsLogic(ctx, svcCtx).GetUserPerms(&types.GetUserPermsReq{UserId: userId}) require.NoError(t, err) require.NotNil(t, resp) require.Len(t, resp.Perms, 1) assert.Equal(t, permId, resp.Perms[0].PermId) assert.Equal(t, consts.PermEffectAllow, resp.Perms[0].Effect) } // TC-1258: 用户查询自己的权限覆盖(包含 ALLOW 和 DENY) func TestGetUserPerms_SelfQuery(t *testing.T) { bootstrapCtx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() username := testutil.UniqueId() userId := insertTestUser(t, bootstrapCtx, username, testutil.HashPassword("pass")) mId := insertTestMember(t, svcCtx, "test_product", userId) permId1 := insertTestPerm(t, svcCtx, "test_product") permId2 := insertTestPerm(t, svcCtx, "test_product") insertUserPerm(t, svcCtx, userId, permId1, consts.PermEffectAllow) insertUserPerm(t, svcCtx, userId, permId2, consts.PermEffectDeny) t.Cleanup(func() { testutil.CleanTableByField(bootstrapCtx, conn, "`sys_user_perm`", "userId", userId) testutil.CleanTable(bootstrapCtx, conn, "`sys_product_member`", mId) testutil.CleanTable(bootstrapCtx, conn, "`sys_user`", userId) testutil.CleanTable(bootstrapCtx, conn, "`sys_perm`", permId1, permId2) }) // caller == target(isSelf 分支,跳过 RequireProductAdminFor + CheckManageAccess) selfCtx := ctxhelper.CustomCtx(&loaders.UserDetails{ UserId: userId, Username: username, MemberType: consts.MemberTypeMember, ProductCode: "test_product", Status: consts.StatusEnabled, }) resp, err := NewGetUserPermsLogic(selfCtx, svcCtx).GetUserPerms(&types.GetUserPermsReq{UserId: userId}) require.NoError(t, err) require.NotNil(t, resp) assert.Len(t, resp.Perms, 2, "应返回 ALLOW 和 DENY 两条记录") } // TC-1259: 产品 ADMIN 查同产品 MEMBER 的权限覆盖 func TestGetUserPerms_AdminQueriesMember(t *testing.T) { bootstrapCtx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() username := testutil.UniqueId() userId := insertTestUser(t, bootstrapCtx, username, testutil.HashPassword("pass")) mId := insertTestMember(t, svcCtx, "test_product", userId) // MEMBER 类型 permId := insertTestPerm(t, svcCtx, "test_product") insertUserPerm(t, svcCtx, userId, permId, consts.PermEffectAllow) t.Cleanup(func() { testutil.CleanTableByField(bootstrapCtx, conn, "`sys_user_perm`", "userId", userId) testutil.CleanTable(bootstrapCtx, conn, "`sys_product_member`", mId) testutil.CleanTable(bootstrapCtx, conn, "`sys_user`", userId) testutil.CleanTable(bootstrapCtx, conn, "`sys_perm`", permId) }) adminCtx := ctxhelper.AdminCtx("test_product") resp, err := NewGetUserPermsLogic(adminCtx, svcCtx).GetUserPerms(&types.GetUserPermsReq{UserId: userId}) require.NoError(t, err) require.NotNil(t, resp) assert.Len(t, resp.Perms, 1) assert.Equal(t, permId, resp.Perms[0].PermId) } // TC-1260: 产品 ADMIN 查同级 ADMIN 被拒绝(permsLevel 等级相同时 CheckManageAccess 拒绝) func TestGetUserPerms_AdminQueriesSameLevelAdmin_Forbidden(t *testing.T) { bootstrapCtx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() // 目标:ADMIN 类型成员 targetUsername := testutil.UniqueId() targetUserId := insertTestUser(t, bootstrapCtx, targetUsername, testutil.HashPassword("pass")) mId := insertTestMemberWithType(t, svcCtx, "test_product", targetUserId, consts.MemberTypeAdmin) t.Cleanup(func() { testutil.CleanTable(bootstrapCtx, conn, "`sys_product_member`", mId) testutil.CleanTable(bootstrapCtx, conn, "`sys_user`", targetUserId) }) // AdminCtx UserId=2,无任何角色 → callerNoRole=true → CheckManageAccess 拒绝(等级不低于目标) adminCtx := ctxhelper.AdminCtx("test_product") _, err := NewGetUserPermsLogic(adminCtx, svcCtx).GetUserPerms(&types.GetUserPermsReq{UserId: targetUserId}) require.Error(t, err) var ce *response.CodeError require.True(t, errors.As(err, &ce)) assert.Equal(t, 403, ce.Code()) } // TC-1261: 普通 MEMBER 查他人被拒绝(RequireProductAdminFor 前置拦截) func TestGetUserPerms_MemberQueriesOther_Forbidden(t *testing.T) { bootstrapCtx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() otherUsername := testutil.UniqueId() otherUserId := insertTestUser(t, bootstrapCtx, otherUsername, testutil.HashPassword("pass")) mId := insertTestMember(t, svcCtx, "test_product", otherUserId) t.Cleanup(func() { testutil.CleanTable(bootstrapCtx, conn, "`sys_product_member`", mId) testutil.CleanTable(bootstrapCtx, conn, "`sys_user`", otherUserId) }) memberCtx := ctxhelper.MemberCtx("test_product") _, err := NewGetUserPermsLogic(memberCtx, svcCtx).GetUserPerms(&types.GetUserPermsReq{UserId: otherUserId}) require.Error(t, err) var ce *response.CodeError require.True(t, errors.As(err, &ce)) assert.Equal(t, 403, ce.Code()) assert.Contains(t, ce.Error(), "仅超级管理员或该产品的管理员可执行此操作") } // TC-1262: 非 ADMIN 查不存在的 userId 必须 403(RequireProductAdminFor 先于实体读取,消除枚举 oracle) func TestGetUserPerms_NonAdmin_NonExistentUser_Returns403(t *testing.T) { svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) memberCtx := ctxhelper.MemberCtx("test_product") _, err := NewGetUserPermsLogic(memberCtx, svcCtx).GetUserPerms(&types.GetUserPermsReq{UserId: 999999999}) require.Error(t, err) var ce *response.CodeError require.True(t, errors.As(err, &ce)) assert.Equal(t, 403, ce.Code(), "必须是 403 而非 404,防止枚举 userId 存在性") } // TC-1263: ADMIN 查不是当前产品成员的用户被拒绝 func TestGetUserPerms_AdminQueriesNonMember_Forbidden(t *testing.T) { bootstrapCtx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() // 创建用户,但不加入 test_product username := testutil.UniqueId() userId := insertTestUser(t, bootstrapCtx, username, testutil.HashPassword("pass")) t.Cleanup(func() { testutil.CleanTable(bootstrapCtx, conn, "`sys_user`", userId) }) adminCtx := ctxhelper.AdminCtx("test_product") _, err := NewGetUserPermsLogic(adminCtx, svcCtx).GetUserPerms(&types.GetUserPermsReq{UserId: userId}) require.Error(t, err) var ce *response.CodeError require.True(t, errors.As(err, &ce)) assert.Equal(t, 403, ce.Code()) } // TC-1264: nil UserDetails(模拟无 JWT)返回 401 func TestGetUserPerms_NoAuth_Returns401(t *testing.T) { svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) _, err := NewGetUserPermsLogic(ctxhelper.CustomCtx(nil), svcCtx).GetUserPerms(&types.GetUserPermsReq{UserId: 1}) require.Error(t, err) var ce *response.CodeError require.True(t, errors.As(err, &ce)) assert.Equal(t, 401, ce.Code()) }