| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 |
- package role
- import (
- "errors"
- "testing"
- "time"
- permModel "perms-system-server/internal/model/perm"
- roleModel "perms-system-server/internal/model/role"
- "perms-system-server/internal/model/roleperm"
- "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"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- )
- // ---------------------------------------------------------------------------
- // 覆盖目标:审计 M-N3 修复 —— RoleDetail 必须消除"角色不存在 vs 跨产品访问"的枚举 oracle。
- // 旧实现:存在但在别的产品 → 403;不存在 → 404。遍历 id 即可画出跨产品 roleId 分布图。
- // 新契约:
- // - 非超管跨产品访问 → 404 "角色不存在"(与真正 NotFound 响应体完全一致)
- // - 超管仍可跨产品访问(审计/运维需要)
- // ---------------------------------------------------------------------------
- // TC-1000: M-N3 —— 非超管访问别的产品的 role 必须 404,与 "id 不存在" 响应体一致。
- func TestRoleDetail_MN3_CrossProductReturns404NotFound(t *testing.T) {
- ctx := ctxhelper.SuperAdminCtx()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- // 插入别的产品("test_product2")下的 role
- otherProduct := "mn3_other_" + testutil.UniqueId()
- roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
- ProductCode: otherProduct, Name: "mn3_role_" + testutil.UniqueId(),
- Remark: "mn3", Status: 1, PermsLevel: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- roleId, _ := roleRes.LastInsertId()
- t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_role`", roleId) })
- // Admin 身份在 "test_product" 下访问 "mn3_other_*" 的 roleId
- adminCtx := ctxhelper.AdminCtx("test_product")
- resp, err := NewRoleDetailLogic(adminCtx, svcCtx).RoleDetail(&types.RoleDetailReq{Id: roleId})
- assert.Nil(t, resp)
- require.Error(t, err)
- var ce *response.CodeError
- require.True(t, errors.As(err, &ce))
- assert.Equal(t, 404, ce.Code(),
- "M-N3:跨产品访问必须 404,不得以 403 暴露存在性")
- assert.Equal(t, "角色不存在", ce.Error(),
- "M-N3:响应文案必须与 'id 真实不存在' 完全一致,彻底关闭枚举 oracle")
- }
- // TC-1001: M-N3 对照 —— "id 不存在" 的响应必须与 "跨产品访问" 完全一致(code 与 body)。
- func TestRoleDetail_MN3_NotFoundAndCrossProduct_Indistinguishable(t *testing.T) {
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- ctx := ctxhelper.SuperAdminCtx()
- now := time.Now().Unix()
- // 预埋一条别的产品的角色
- otherProduct := "mn3_cmp_" + testutil.UniqueId()
- roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
- ProductCode: otherProduct, Name: "mn3cmp_" + testutil.UniqueId(),
- Remark: "", Status: 1, PermsLevel: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- existingId, _ := roleRes.LastInsertId()
- t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_role`", existingId) })
- adminCtx := ctxhelper.AdminCtx("test_product")
- // (A) 跨产品访问真实存在的 id
- _, errCross := NewRoleDetailLogic(adminCtx, svcCtx).RoleDetail(&types.RoleDetailReq{Id: existingId})
- require.Error(t, errCross)
- var ceA *response.CodeError
- require.True(t, errors.As(errCross, &ceA))
- // (B) 访问一个肯定不存在的 id(id 选在 existingId + 很大偏移,规避数据库递增到该值)
- _, errAbsent := NewRoleDetailLogic(adminCtx, svcCtx).RoleDetail(
- &types.RoleDetailReq{Id: existingId + 999_999_999},
- )
- require.Error(t, errAbsent)
- var ceB *response.CodeError
- require.True(t, errors.As(errAbsent, &ceB))
- // 响应码与文案必须完全一致,不给任何侧信道
- assert.Equal(t, ceB.Code(), ceA.Code(),
- "M-N3:跨产品 vs id 不存在必须返回相同 code")
- assert.Equal(t, ceB.Error(), ceA.Error(),
- "M-N3:跨产品 vs id 不存在必须返回相同 body")
- }
- // TC-1002: M-N3 —— 超管跨产品仍然可以正常访问(审计/运维路径不能被误伤)。
- func TestRoleDetail_MN3_SuperAdminCanStillAccessCrossProduct(t *testing.T) {
- ctx := ctxhelper.SuperAdminCtx()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- otherProduct := "mn3_sa_" + testutil.UniqueId()
- roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
- ProductCode: otherProduct, Name: "mn3sa_" + testutil.UniqueId(),
- Remark: "sa_cross", Status: 1, PermsLevel: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- roleId, _ := roleRes.LastInsertId()
- permRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: otherProduct, Name: "mn3sa_p_" + testutil.UniqueId(), Code: testutil.UniqueId(),
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- permId, _ := permRes.LastInsertId()
- rpRes, err := svcCtx.SysRolePermModel.Insert(ctx, &roleperm.SysRolePerm{
- RoleId: roleId, PermId: permId, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- rpId, _ := rpRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_role_perm`", rpId)
- testutil.CleanTable(ctx, conn, "`sys_perm`", permId)
- testutil.CleanTable(ctx, conn, "`sys_role`", roleId)
- })
- // 超管在 "test_product" 当前身份,但跨产品访问 "mn3_sa_*" 的 role,应允许
- resp, err := NewRoleDetailLogic(ctx, svcCtx).RoleDetail(&types.RoleDetailReq{Id: roleId})
- require.NoError(t, err, "超管必须能跨产品查看 role,支撑审计/运维路径")
- require.NotNil(t, resp)
- assert.Equal(t, roleId, resp.Id)
- assert.Equal(t, otherProduct, resp.ProductCode)
- assert.Contains(t, resp.PermIds, permId)
- }
|