| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666 |
- package user
- import (
- "context"
- "errors"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "math"
- "perms-system-server/internal/consts"
- "perms-system-server/internal/loaders"
- "perms-system-server/internal/middleware"
- permModel "perms-system-server/internal/model/perm"
- productModel "perms-system-server/internal/model/product"
- "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"
- "testing"
- "time"
- )
- type userPermRow struct {
- Id int64 `db:"id"`
- UserId int64 `db:"userId"`
- PermId int64 `db:"permId"`
- Effect string `db:"effect"`
- }
- func findUserPerms(t *testing.T, ctx context.Context, userId int64) []userPermRow {
- t.Helper()
- conn := testutil.GetTestSqlConn()
- var rows []userPermRow
- require.NoError(t, conn.QueryRowsCtx(ctx, &rows,
- "SELECT `id`,`userId`,`permId`,`effect` FROM `sys_user_perm` WHERE `userId`=?", userId))
- return rows
- }
- func insertTestPerm(t *testing.T, svcCtx *svc.ServiceContext, productCode string) int64 {
- t.Helper()
- now := time.Now().Unix()
- res, err := svcCtx.SysPermModel.Insert(ctxhelper.SuperAdminCtx(), &permModel.SysPerm{
- ProductCode: productCode,
- Name: "perm_" + testutil.UniqueId(),
- Code: "code_" + testutil.UniqueId(),
- Status: 1,
- CreateTime: now,
- UpdateTime: now,
- })
- require.NoError(t, err)
- id, _ := res.LastInsertId()
- return id
- }
- // TC-0192: 正常ALLOW
- func TestSetUserPerms_Allow(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)
- p1 := insertTestPerm(t, svcCtx, "test_product")
- p2 := insertTestPerm(t, svcCtx, "test_product")
- 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`", p1, p2)
- })
- logic := NewSetUserPermsLogic(ctx, svcCtx)
- err := logic.SetUserPerms(&types.SetPermsReq{
- UserId: userId,
- Perms: []types.UserPermItem{
- {PermId: p1, Effect: "ALLOW"},
- {PermId: p2, Effect: "ALLOW"},
- },
- })
- require.NoError(t, err)
- perms := findUserPerms(t, ctx, userId)
- assert.Len(t, perms, 2)
- for _, p := range perms {
- assert.Equal(t, "ALLOW", p.Effect)
- }
- }
- // TC-0194: DENY权限
- func TestSetUserPerms_Deny(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)
- p1 := insertTestPerm(t, svcCtx, "test_product")
- 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`", p1)
- })
- logic := NewSetUserPermsLogic(ctx, svcCtx)
- err := logic.SetUserPerms(&types.SetPermsReq{
- UserId: userId,
- Perms: []types.UserPermItem{
- {PermId: p1, Effect: "DENY"},
- },
- })
- require.NoError(t, err)
- perms := findUserPerms(t, ctx, userId)
- require.Len(t, perms, 1)
- assert.Equal(t, "DENY", perms[0].Effect)
- assert.Equal(t, p1, perms[0].PermId)
- }
- // TC-0193: 用户不存在
- func TestSetUserPerms_UserNotFound(t *testing.T) {
- ctx := ctxhelper.SuperAdminCtx()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- logic := NewSetUserPermsLogic(ctx, svcCtx)
- err := logic.SetUserPerms(&types.SetPermsReq{
- UserId: 999999999,
- Perms: []types.UserPermItem{
- {PermId: 1, Effect: "ALLOW"},
- },
- })
- require.Error(t, err)
- var codeErr *response.CodeError
- require.True(t, errors.As(err, &codeErr))
- assert.Equal(t, 404, codeErr.Code())
- assert.Equal(t, "用户不存在", codeErr.Error())
- }
- // TC-0195: 清空权限
- func TestSetUserPerms_EmptyPerms_ClearsAll(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)
- p1 := insertTestPerm(t, svcCtx, "test_product")
- 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`", p1)
- })
- logic := NewSetUserPermsLogic(ctx, svcCtx)
- err := logic.SetUserPerms(&types.SetPermsReq{
- UserId: userId,
- Perms: []types.UserPermItem{
- {PermId: p1, Effect: "ALLOW"},
- },
- })
- require.NoError(t, err)
- err = logic.SetUserPerms(&types.SetPermsReq{
- UserId: userId,
- Perms: []types.UserPermItem{},
- })
- require.NoError(t, err)
- perms := findUserPerms(t, ctx, userId)
- assert.Empty(t, perms)
- }
- // TC-0196: 无效Effect值
- func TestSetUserPerms_InvalidEffect(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)
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_product_member`", mId)
- testutil.CleanTable(ctx, conn, "`sys_user`", userId)
- })
- logic := NewSetUserPermsLogic(ctx, svcCtx)
- err := logic.SetUserPerms(&types.SetPermsReq{
- UserId: userId,
- Perms: []types.UserPermItem{
- {PermId: 1, Effect: "INVALID"},
- },
- })
- require.Error(t, err)
- var codeErr *response.CodeError
- require.True(t, errors.As(err, &codeErr))
- assert.Equal(t, 400, codeErr.Code())
- assert.Contains(t, codeErr.Error(), "effect值无效")
- }
- // TC-0197: PermId不存在
- func TestSetUserPerms_PermNotExists(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)
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_product_member`", mId)
- testutil.CleanTable(ctx, conn, "`sys_user`", userId)
- })
- logic := NewSetUserPermsLogic(ctx, svcCtx)
- err := logic.SetUserPerms(&types.SetPermsReq{
- UserId: userId,
- Perms: []types.UserPermItem{
- {PermId: 999999999, Effect: "ALLOW"},
- },
- })
- require.Error(t, err)
- var codeErr *response.CodeError
- require.True(t, errors.As(err, &codeErr))
- assert.Equal(t, 400, codeErr.Code())
- assert.Contains(t, codeErr.Error(), "无效的权限ID")
- }
- // TC-0198: 权限不属于当前产品
- func TestSetUserPerms_PermBelongsToOtherProduct(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)
- otherPerm := insertTestPerm(t, svcCtx, "other_product")
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_product_member`", mId)
- testutil.CleanTable(ctx, conn, "`sys_user`", userId)
- testutil.CleanTable(ctx, conn, "`sys_perm`", otherPerm)
- })
- logic := NewSetUserPermsLogic(ctx, svcCtx)
- err := logic.SetUserPerms(&types.SetPermsReq{
- UserId: userId,
- Perms: []types.UserPermItem{
- {PermId: otherPerm, Effect: "ALLOW"},
- },
- })
- require.Error(t, err)
- var codeErr *response.CodeError
- require.True(t, errors.As(err, &codeErr))
- assert.Equal(t, 400, codeErr.Code())
- assert.Contains(t, codeErr.Error(), "其他产品的权限")
- }
- // TC-0210: 同一权限ID同时为ALLOW和DENY被拒绝
- func TestSetUserPerms_ConflictingEffects(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)
- p1 := insertTestPerm(t, svcCtx, "test_product")
- 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`", p1)
- })
- logic := NewSetUserPermsLogic(ctx, svcCtx)
- err := logic.SetUserPerms(&types.SetPermsReq{
- UserId: userId,
- Perms: []types.UserPermItem{
- {PermId: p1, Effect: "ALLOW"},
- {PermId: p1, Effect: "DENY"},
- },
- })
- require.Error(t, err)
- var codeErr *response.CodeError
- require.True(t, errors.As(err, &codeErr))
- assert.Equal(t, 400, codeErr.Code())
- assert.Contains(t, codeErr.Error(), "同一权限ID不能同时为 ALLOW 和 DENY")
- }
- // TC-0211: 重复的权限ID相同Effect被去重
- func TestSetUserPerms_DuplicatePermDedup(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)
- p1 := insertTestPerm(t, svcCtx, "test_product")
- 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`", p1)
- })
- logic := NewSetUserPermsLogic(ctx, svcCtx)
- err := logic.SetUserPerms(&types.SetPermsReq{
- UserId: userId,
- Perms: []types.UserPermItem{
- {PermId: p1, Effect: "ALLOW"},
- {PermId: p1, Effect: "ALLOW"},
- },
- })
- require.NoError(t, err)
- perms := findUserPerms(t, ctx, userId)
- assert.Len(t, perms, 1, "重复的权限ID应被去重,只插入一条")
- assert.Equal(t, "ALLOW", perms[0].Effect)
- }
- // TC-0212: 已禁用的权限不能被设置
- func TestSetUserPerms_DisabledPermRejected(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)
- now := time.Now().Unix()
- res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: "test_product",
- Name: "disabled_perm_" + testutil.UniqueId(),
- Code: "disabled_" + testutil.UniqueId(),
- Status: 2,
- CreateTime: now,
- UpdateTime: now,
- })
- require.NoError(t, err)
- disabledPermId, _ := res.LastInsertId()
- 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`", disabledPermId)
- })
- logic := NewSetUserPermsLogic(ctx, svcCtx)
- err = logic.SetUserPerms(&types.SetPermsReq{
- UserId: userId,
- Perms: []types.UserPermItem{
- {PermId: disabledPermId, Effect: "ALLOW"},
- },
- })
- require.Error(t, err)
- var codeErr *response.CodeError
- require.True(t, errors.As(err, &codeErr))
- assert.Equal(t, 400, codeErr.Code())
- assert.Contains(t, codeErr.Error(), "已被禁用")
- }
- // TC-0199: 目标用户不是当前产品成员时拒绝设置权限(修复验证)
- func TestSetUserPerms_NonMemberRejected(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"))
- t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) })
- logic := NewSetUserPermsLogic(ctx, svcCtx)
- err := logic.SetUserPerms(&types.SetPermsReq{
- UserId: userId,
- Perms: []types.UserPermItem{},
- })
- require.Error(t, err)
- var codeErr2 *response.CodeError
- require.True(t, errors.As(err, &codeErr2))
- assert.Equal(t, 400, codeErr2.Code())
- assert.Contains(t, codeErr2.Error(), "不是当前产品的成员")
- }
- type lyingSysPermModel struct {
- permModel.SysPermModel
- lyingProductCode string
- }
- func (m *lyingSysPermModel) FindByIds(ctx context.Context, ids []int64) ([]*permModel.SysPerm, error) {
- real, err := m.SysPermModel.FindByIds(ctx, ids)
- if err != nil {
- return nil, err
- }
- for _, p := range real {
- p.ProductCode = m.lyingProductCode
- p.Status = 1
- }
- return real, nil
- }
- // TC-0988: TOCTOU 复核 —— 前置检查通过但实际 Disabled,事务末 COUNT 必须触发 409 回滚。
- func TestSetUserPerms_L4_TOCTOU_CountMismatch_RollsBackWith409(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)
- // 直接在 DB 里塞一个 status=Disabled 的 perm,模拟 SyncPermissions 已经提交
- // 把这个 perm 落盘为 Disabled 的状态。
- now := time.Now().Unix()
- res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: "test_product",
- Name: "l4_disabled_" + testutil.UniqueId(),
- Code: "l4_dis_" + testutil.UniqueId(),
- Status: 2, // Disabled
- CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- disabledPermId, _ := res.LastInsertId()
- 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`", disabledPermId)
- })
- // 装饰 SysPermModel:让 FindByIds 撒谎(Status=1, productCode=test_product)。
- svcCtx.SysPermModel = &lyingSysPermModel{
- SysPermModel: svcCtx.SysPermModel,
- lyingProductCode: "test_product",
- }
- err = NewSetUserPermsLogic(ctx, svcCtx).SetUserPerms(&types.SetPermsReq{
- UserId: userId,
- Perms: []types.UserPermItem{{PermId: disabledPermId, Effect: "ALLOW"}},
- })
- require.Error(t, err, "前置通过但 DB 实际 Disabled 时,事务末 COUNT 必须触发 409")
- var ce *response.CodeError
- require.True(t, errors.As(err, &ce))
- assert.Equal(t, 409, ce.Code(),
- "TOCTOU 复核必须返回 409 Conflict;若仍是 200/4xx 说明复核 COUNT 被移除,"+
- "脏 user_perm 会被真实落盘")
- assert.Contains(t, ce.Error(), "已被禁用",
- "错误文案必须明示'部分权限在提交时已被禁用',供前端判定是否重试")
- // 最关键的断言:脏行必须不可能落盘。
- leftover := findUserPerms(t, ctx, userId)
- assert.Empty(t, leftover,
- "事务必须回滚;如果发现 sys_user_perm 有脏行,说明 COUNT 复核失效或"+
- "事务隔离性被破坏,loadPerms 的 status=1 过滤能兜底但会绕开链")
- }
- // TC-0989: 正向基线 —— 所有 perm 真实 Enabled 时,不得被 复核误杀。
- // 这条显式"不回滚"的断言防止未来有人把 COUNT 改成 "!=" 逻辑或把阈值改错。
- func TestSetUserPerms_L4_AllEnabled_CountPasses(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)
- p1 := insertTestPerm(t, svcCtx, "test_product")
- p2 := insertTestPerm(t, svcCtx, "test_product")
- 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`", p1, p2)
- })
- err := NewSetUserPermsLogic(ctx, svcCtx).SetUserPerms(&types.SetPermsReq{
- UserId: userId,
- Perms: []types.UserPermItem{
- {PermId: p1, Effect: "ALLOW"},
- {PermId: p2, Effect: "DENY"},
- },
- })
- require.NoError(t, err, "不得误杀正常写入;一旦误报会把正常管理操作变 409")
- rows := findUserPerms(t, ctx, userId)
- assert.Len(t, rows, 2, "两条 user_perm 必须落盘")
- }
- func TestSetUserPerms_MemberCannotSelfEscalate(t *testing.T) {
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- bootstrap := context.Background()
- code := testutil.UniqueId()
- pRes, err := svcCtx.SysProductModel.Insert(bootstrap, &productModel.SysProduct{
- Code: code, Name: "p_" + code, AppKey: code + "_k", AppSecret: "s",
- Status: consts.StatusEnabled, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- username := testutil.UniqueId()
- userId := insertTestUser(t, bootstrap, username, testutil.HashPassword("pw"))
- mId := insertTestMember(t, svcCtx, code, userId)
- permRes, err := svcCtx.SysPermModel.Insert(bootstrap, &permModel.SysPerm{
- ProductCode: code, Name: "escalate_p", Code: "esc_" + testutil.UniqueId(),
- Status: consts.StatusEnabled, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- permId, _ := permRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTableByField(bootstrap, conn, "`sys_user_perm`", "userId", userId)
- testutil.CleanTable(bootstrap, conn, "`sys_product_member`", mId)
- testutil.CleanTable(bootstrap, conn, "`sys_perm`", permId)
- testutil.CleanTable(bootstrap, conn, "`sys_user`", userId)
- testutil.CleanTable(bootstrap, conn, "`sys_product`", pId)
- })
- // caller = 目标用户本人,MemberType=MEMBER(非 ADMIN)
- callerCtx := middleware.WithUserDetails(context.Background(), &loaders.UserDetails{
- UserId: userId, Username: username,
- IsSuperAdmin: false,
- MemberType: consts.MemberTypeMember,
- Status: consts.StatusEnabled,
- ProductCode: code,
- DeptId: 1,
- DeptPath: "/1/",
- MinPermsLevel: math.MaxInt64,
- })
- err = NewSetUserPermsLogic(callerCtx, svcCtx).SetUserPerms(&types.SetPermsReq{
- UserId: userId, // 给自己
- Perms: []types.UserPermItem{{PermId: permId, Effect: consts.PermEffectAllow}},
- })
- require.Error(t, err, "MEMBER 不得自我授权")
- var ce *response.CodeError
- require.True(t, errors.As(err, &ce))
- assert.Equal(t, 403, ce.Code())
- assert.Contains(t, ce.Error(), "仅超级管理员或该产品的管理员可执行此操作")
- // 二次确认:没有任何 user_perm 记录被写入
- rows := findUserPerms(t, bootstrap, userId)
- assert.Len(t, rows, 0, "被拒绝的 SetUserPerms 不得在 DB 残留任何个性化权限")
- }
- // TC-0744: -A 修复回归 —— DEVELOPER 调用者(非 ADMIN)同样被拦截,即便目标不是自己。
- func TestSetUserPerms_DeveloperCallerRejected(t *testing.T) {
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- bootstrap := context.Background()
- now := time.Now().Unix()
- code := testutil.UniqueId()
- pRes, err := svcCtx.SysProductModel.Insert(bootstrap, &productModel.SysProduct{
- Code: code, Name: "p_" + code, AppKey: code + "_k", AppSecret: "s",
- Status: consts.StatusEnabled, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- targetUsername := "target_" + testutil.UniqueId()
- targetId := insertTestUser(t, bootstrap, targetUsername, testutil.HashPassword("pw"))
- mId := insertTestMember(t, svcCtx, code, targetId)
- t.Cleanup(func() {
- testutil.CleanTable(bootstrap, conn, "`sys_product_member`", mId)
- testutil.CleanTable(bootstrap, conn, "`sys_user`", targetId)
- testutil.CleanTable(bootstrap, conn, "`sys_product`", pId)
- })
- devCtx := middleware.WithUserDetails(context.Background(), &loaders.UserDetails{
- UserId: 777777, Username: "dev_caller",
- MemberType: consts.MemberTypeDeveloper, Status: consts.StatusEnabled,
- ProductCode: code, DeptId: 1, DeptPath: "/1/", MinPermsLevel: math.MaxInt64,
- })
- err = NewSetUserPermsLogic(devCtx, svcCtx).SetUserPerms(&types.SetPermsReq{
- UserId: targetId, Perms: []types.UserPermItem{},
- })
- 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-0745: -A 正向回归 —— 同产品 ADMIN 操作合法 MEMBER 目标(非自己)依旧放行。
- func TestSetUserPerms_ProductAdminStillWorks(t *testing.T) {
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- bootstrap := context.Background()
- now := time.Now().Unix()
- code := testutil.UniqueId()
- pRes, err := svcCtx.SysProductModel.Insert(bootstrap, &productModel.SysProduct{
- Code: code, Name: "p_" + code, AppKey: code + "_k", AppSecret: "s",
- Status: consts.StatusEnabled, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- targetId := insertTestUser(t, bootstrap, "tgt_"+testutil.UniqueId(), testutil.HashPassword("pw"))
- mId := insertTestMember(t, svcCtx, code, targetId)
- permRes, err := svcCtx.SysPermModel.Insert(bootstrap, &permModel.SysPerm{
- ProductCode: code, Name: "ok_p", Code: "ok_" + testutil.UniqueId(),
- Status: consts.StatusEnabled, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- permId, _ := permRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTableByField(bootstrap, conn, "`sys_user_perm`", "userId", targetId)
- testutil.CleanTable(bootstrap, conn, "`sys_product_member`", mId)
- testutil.CleanTable(bootstrap, conn, "`sys_perm`", permId)
- testutil.CleanTable(bootstrap, conn, "`sys_user`", targetId)
- testutil.CleanTable(bootstrap, conn, "`sys_product`", pId)
- })
- adminCtx := middleware.WithUserDetails(context.Background(), &loaders.UserDetails{
- UserId: 999999, Username: "admin_caller",
- MemberType: consts.MemberTypeAdmin, Status: consts.StatusEnabled,
- ProductCode: code, DeptId: 1, DeptPath: "/1/", MinPermsLevel: math.MaxInt64,
- })
- err = NewSetUserPermsLogic(adminCtx, svcCtx).SetUserPerms(&types.SetPermsReq{
- UserId: targetId,
- Perms: []types.UserPermItem{{PermId: permId, Effect: consts.PermEffectAllow}},
- })
- require.NoError(t, err, "产品 ADMIN 正常路径必须放行")
- rows := findUserPerms(t, bootstrap, targetId)
- assert.Len(t, rows, 1, "ADMIN 授权后 DB 应有 1 条 user_perm")
- }
|