| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438 |
- package server
- import (
- "context"
- "database/sql"
- "fmt"
- "testing"
- "time"
- authHelper "perms-system-server/internal/logic/auth"
- deptModel "perms-system-server/internal/model/dept"
- permModel "perms-system-server/internal/model/perm"
- productModel "perms-system-server/internal/model/product"
- memberModel "perms-system-server/internal/model/productmember"
- roleModel "perms-system-server/internal/model/role"
- rolePermModel "perms-system-server/internal/model/roleperm"
- userModel "perms-system-server/internal/model/user"
- userPermModel "perms-system-server/internal/model/userperm"
- userRoleModel "perms-system-server/internal/model/userrole"
- "perms-system-server/internal/svc"
- "perms-system-server/internal/testutil"
- "perms-system-server/pb"
- "github.com/golang-jwt/jwt/v4"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "golang.org/x/crypto/bcrypt"
- "google.golang.org/grpc/codes"
- "google.golang.org/grpc/status"
- )
- func bcryptHash(t *testing.T, plaintext string) string {
- t.Helper()
- h, err := bcrypt.GenerateFromPassword([]byte(plaintext), bcrypt.MinCost)
- require.NoError(t, err)
- return string(h)
- }
- // ---------- SyncPermissions ----------
- // TC-0230: 正常同步
- func TestSyncPermissions_Normal(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
- Code: uid, Name: "test_prod", AppKey: uid, AppSecret: bcryptHash(t, "secret1"),
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTableByField(ctx, conn, "`sys_perm`", "productCode", uid)
- testutil.CleanTable(ctx, conn, "`sys_product`", pId)
- })
- srv := NewPermServer(svcCtx)
- resp, err := srv.SyncPermissions(ctx, &pb.SyncPermissionsReq{
- AppKey: uid,
- AppSecret: "secret1",
- Perms: []*pb.PermItem{
- {Code: "perm_a", Name: "Perm A", Remark: "remark_a"},
- {Code: "perm_b", Name: "Perm B", Remark: "remark_b"},
- },
- })
- require.NoError(t, err)
- assert.Equal(t, int64(2), resp.Added)
- assert.Equal(t, int64(0), resp.Updated)
- assert.Equal(t, int64(0), resp.Disabled)
- resp2, err := srv.SyncPermissions(ctx, &pb.SyncPermissionsReq{
- AppKey: uid,
- AppSecret: "secret1",
- Perms: []*pb.PermItem{
- {Code: "perm_a", Name: "Perm A Updated", Remark: "remark_a"},
- },
- })
- require.NoError(t, err)
- assert.Equal(t, int64(0), resp2.Added)
- assert.Equal(t, int64(1), resp2.Updated)
- assert.Equal(t, int64(1), resp2.Disabled)
- }
- // TC-0231: appKey无效
- func TestSyncPermissions_InvalidAppKey(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- srv := NewPermServer(svcCtx)
- _, err := srv.SyncPermissions(ctx, &pb.SyncPermissionsReq{
- AppKey: "nonexistent_key",
- AppSecret: "any",
- Perms: []*pb.PermItem{{Code: "c", Name: "n"}},
- })
- require.Error(t, err)
- assert.Equal(t, codes.Unauthenticated, status.Code(err))
- assert.Equal(t, "无效的appKey", status.Convert(err).Message())
- }
- // TC-0232: appSecret错误
- func TestSyncPermissions_WrongAppSecret(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
- Code: uid, Name: "test_prod", AppKey: uid, AppSecret: bcryptHash(t, "real_secret"),
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_product`", pId)
- })
- srv := NewPermServer(svcCtx)
- _, err = srv.SyncPermissions(ctx, &pb.SyncPermissionsReq{
- AppKey: uid,
- AppSecret: "wrong_secret",
- Perms: []*pb.PermItem{{Code: "c", Name: "n"}},
- })
- require.Error(t, err)
- assert.Equal(t, codes.Unauthenticated, status.Code(err))
- assert.Equal(t, "appSecret验证失败", status.Convert(err).Message())
- }
- // TC-0233: 产品已禁用
- func TestSyncPermissions_ProductDisabled(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
- Code: uid, Name: "test_prod", AppKey: uid, AppSecret: bcryptHash(t, "secret1"),
- Status: 2, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_product`", pId)
- })
- srv := NewPermServer(svcCtx)
- _, err = srv.SyncPermissions(ctx, &pb.SyncPermissionsReq{
- AppKey: uid,
- AppSecret: "secret1",
- Perms: []*pb.PermItem{{Code: "c", Name: "n"}},
- })
- require.Error(t, err)
- assert.Equal(t, codes.PermissionDenied, status.Code(err))
- assert.Equal(t, "产品已被禁用", status.Convert(err).Message())
- }
- // ---------- Login ----------
- // TC-0235: 正常登录(普通用户+productCode)
- func TestLogin_Normal(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
- Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
- Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- uId, _ := uRes.LastInsertId()
- pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
- Code: uid, Name: "test_prod", AppKey: uid + "_k", AppSecret: "s1",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- pmRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
- ProductCode: uid, UserId: uId, MemberType: "MEMBER", Status: 1,
- CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pmId, _ := pmRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_product_member`", pmId)
- testutil.CleanTable(ctx, conn, "`sys_product`", pId)
- testutil.CleanTable(ctx, conn, "`sys_user`", uId)
- })
- srv := NewPermServer(svcCtx)
- resp, err := srv.Login(ctx, &pb.LoginReq{
- Username: uid,
- Password: "pass123",
- ProductCode: uid,
- })
- require.NoError(t, err)
- assert.NotEmpty(t, resp.AccessToken)
- assert.NotEmpty(t, resp.RefreshToken)
- assert.True(t, resp.Expires > time.Now().Unix(), "expires应为未来的unix时间戳")
- assert.Equal(t, uId, resp.UserId)
- assert.Equal(t, uid, resp.Username)
- // BUG-01: proto定义了nickname字段,实现应返回用户昵称
- assert.Equal(t, "nick", resp.Nickname, "BUG-01: LoginResp.Nickname 应返回用户昵称而非空字符串")
- }
- // TC-0236: 用户不存在
- func TestLogin_UserNotFound(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- srv := NewPermServer(svcCtx)
- _, err := srv.Login(ctx, &pb.LoginReq{
- Username: "nonexistent_user_xyz",
- Password: "any",
- ProductCode: "any_product",
- })
- require.Error(t, err)
- assert.Equal(t, codes.Unauthenticated, status.Code(err))
- assert.Equal(t, "用户名或密码错误", status.Convert(err).Message())
- }
- // TC-0237: 密码错误
- func TestLogin_WrongPassword(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
- Username: uid, Password: testutil.HashPassword("correct_pass"), Nickname: "nick",
- Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- uId, _ := uRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_user`", uId)
- })
- srv := NewPermServer(svcCtx)
- _, err = srv.Login(ctx, &pb.LoginReq{
- Username: uid,
- Password: "wrong_pass",
- ProductCode: "any_product",
- })
- require.Error(t, err)
- assert.Equal(t, codes.Unauthenticated, status.Code(err))
- assert.Equal(t, "用户名或密码错误", status.Convert(err).Message())
- }
- // TC-0238: 账号冻结
- func TestLogin_AccountFrozen(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
- Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
- Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
- Status: 2, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- uId, _ := uRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_user`", uId)
- })
- srv := NewPermServer(svcCtx)
- _, err = srv.Login(ctx, &pb.LoginReq{
- Username: uid,
- Password: "pass123",
- ProductCode: "any_product",
- })
- require.Error(t, err)
- assert.Equal(t, codes.PermissionDenied, status.Code(err))
- assert.Equal(t, "账号已被冻结", status.Convert(err).Message())
- }
- // TC-0239: 超管被拒绝
- func TestLogin_SuperAdminRejected(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
- Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "sa",
- Avatar: sql.NullString{}, IsSuperAdmin: 1, MustChangePassword: 2,
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- uId, _ := uRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_user`", uId)
- })
- srv := NewPermServer(svcCtx)
- _, err = srv.Login(ctx, &pb.LoginReq{
- Username: uid,
- Password: "pass123",
- ProductCode: "any_product",
- })
- require.Error(t, err)
- assert.Equal(t, codes.PermissionDenied, status.Code(err))
- assert.Equal(t, "超级管理员不允许通过产品端登录,请使用管理后台", status.Convert(err).Message())
- }
- // TC-0240: 普通用户+productCode
- func TestLogin_NormalUserWithProductCode(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
- Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
- Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- uId, _ := uRes.LastInsertId()
- pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
- Code: uid, Name: "test_prod", AppKey: uid + "_k", AppSecret: "s1",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- mbrRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
- ProductCode: uid, UserId: uId, MemberType: "MEMBER",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- mbrId, _ := mbrRes.LastInsertId()
- roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
- ProductCode: uid, Name: uid + "_role", Status: 1, PermsLevel: 1,
- CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- roleId, _ := roleRes.LastInsertId()
- pm1Res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: uid, Name: "p1", Code: uid + "_c1",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pm1Id, _ := pm1Res.LastInsertId()
- urRes, err := svcCtx.SysUserRoleModel.Insert(ctx, &userRoleModel.SysUserRole{
- UserId: uId, RoleId: roleId, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- urId, _ := urRes.LastInsertId()
- rpRes, err := svcCtx.SysRolePermModel.Insert(ctx, &rolePermModel.SysRolePerm{
- RoleId: roleId, PermId: pm1Id, 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_user_role`", urId)
- testutil.CleanTable(ctx, conn, "`sys_perm`", pm1Id)
- testutil.CleanTable(ctx, conn, "`sys_role`", roleId)
- testutil.CleanTable(ctx, conn, "`sys_product_member`", mbrId)
- testutil.CleanTable(ctx, conn, "`sys_product`", pId)
- testutil.CleanTable(ctx, conn, "`sys_user`", uId)
- })
- srv := NewPermServer(svcCtx)
- resp, err := srv.Login(ctx, &pb.LoginReq{
- Username: uid,
- Password: "pass123",
- ProductCode: uid,
- })
- require.NoError(t, err)
- assert.Equal(t, "MEMBER", resp.MemberType)
- assert.Contains(t, resp.Perms, uid+"_c1")
- assert.NotEmpty(t, resp.AccessToken)
- assert.NotEmpty(t, resp.RefreshToken)
- }
- // TC-0242: productCode为空
- func TestLogin_EmptyProductCode(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- srv := NewPermServer(svcCtx)
- _, err := srv.Login(ctx, &pb.LoginReq{
- Username: "anyuser",
- Password: "anypass",
- ProductCode: "",
- })
- require.Error(t, err)
- assert.Equal(t, codes.InvalidArgument, status.Code(err))
- assert.Equal(t, "productCode不能为空", status.Convert(err).Message())
- }
- // ---------- RefreshToken ----------
- // TC-0243: 正常刷新(refreshToken原样返回,不重新生成)
- func TestRefreshToken_Normal(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
- Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
- Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- uId, _ := uRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_user`", uId)
- })
- cfg := testutil.GetTestConfig()
- refreshToken, err := authHelper.GenerateRefreshToken(cfg.Auth.RefreshSecret, cfg.Auth.RefreshExpire, uId, "", 0)
- require.NoError(t, err)
- srv := NewPermServer(svcCtx)
- resp, err := srv.RefreshToken(ctx, &pb.RefreshTokenReq{
- RefreshToken: refreshToken,
- })
- require.NoError(t, err)
- assert.NotEmpty(t, resp.AccessToken)
- assert.NotEqual(t, refreshToken, resp.RefreshToken, "M-2修复:refreshToken必须发生轮转")
- newClaims, perr := authHelper.ParseRefreshToken(resp.RefreshToken, cfg.Auth.RefreshSecret)
- require.NoError(t, perr)
- assert.Equal(t, int64(1), newClaims.TokenVersion, "新 refreshToken 必须携带递增后的 tokenVersion")
- assert.True(t, resp.Expires > time.Now().Unix(), "expires应为未来的unix时间戳")
- }
- // TC-0244: token无效
- func TestRefreshToken_InvalidToken(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- srv := NewPermServer(svcCtx)
- _, err := srv.RefreshToken(ctx, &pb.RefreshTokenReq{
- RefreshToken: "invalid.token.string",
- })
- require.Error(t, err)
- assert.Equal(t, codes.Unauthenticated, status.Code(err))
- assert.Equal(t, "refreshToken无效或已过期", status.Convert(err).Message())
- }
- // TC-0245: 账号冻结
- func TestRefreshToken_AccountFrozen(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
- Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
- Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
- Status: 2, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- uId, _ := uRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_user`", uId)
- })
- cfg := testutil.GetTestConfig()
- refreshToken, err := authHelper.GenerateRefreshToken(cfg.Auth.RefreshSecret, cfg.Auth.RefreshExpire, uId, "", 0)
- require.NoError(t, err)
- srv := NewPermServer(svcCtx)
- _, err = srv.RefreshToken(ctx, &pb.RefreshTokenReq{
- RefreshToken: refreshToken,
- })
- require.Error(t, err)
- assert.Equal(t, codes.PermissionDenied, status.Code(err))
- assert.Equal(t, "账号已被冻结", status.Convert(err).Message())
- }
- // TC-0246: productCode回退到claims
- func TestRefreshToken_FallbackToClaimsProductCode(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
- Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
- Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- uId, _ := uRes.LastInsertId()
- pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
- Code: uid, Name: "test_prod", AppKey: uid + "_k", AppSecret: "s1",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- mbrRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
- ProductCode: uid, UserId: uId, MemberType: "MEMBER",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- mbrId, _ := mbrRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_product_member`", mbrId)
- testutil.CleanTable(ctx, conn, "`sys_product`", pId)
- testutil.CleanTable(ctx, conn, "`sys_user`", uId)
- })
- cfg := testutil.GetTestConfig()
- refreshToken, err := authHelper.GenerateRefreshToken(cfg.Auth.RefreshSecret, cfg.Auth.RefreshExpire, uId, uid, 0)
- require.NoError(t, err)
- srv := NewPermServer(svcCtx)
- resp, err := srv.RefreshToken(ctx, &pb.RefreshTokenReq{
- RefreshToken: refreshToken,
- ProductCode: "",
- })
- require.NoError(t, err)
- assert.NotEmpty(t, resp.AccessToken)
- assert.NotEqual(t, refreshToken, resp.RefreshToken, "M-2修复:refreshToken必须发生轮转")
- newClaims, perr := authHelper.ParseRefreshToken(resp.RefreshToken, cfg.Auth.RefreshSecret)
- require.NoError(t, perr)
- assert.Equal(t, int64(1), newClaims.TokenVersion, "新 refreshToken 必须携带递增后的 tokenVersion")
- assert.Equal(t, uid, newClaims.ProductCode, "fallback 分支:应使用 claims.ProductCode")
- }
- // TC-0247: 超管+productCode
- func TestRefreshToken_SuperAdminWithProductCode(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
- Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "sa",
- Avatar: sql.NullString{}, IsSuperAdmin: 1, MustChangePassword: 2,
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- uId, _ := uRes.LastInsertId()
- pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
- Code: uid, Name: "test_prod", AppKey: uid + "_k", AppSecret: "s1",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- pm1Res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: uid, Name: "p1", Code: uid + "_c1",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pm1Id, _ := pm1Res.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_perm`", pm1Id)
- testutil.CleanTable(ctx, conn, "`sys_product`", pId)
- testutil.CleanTable(ctx, conn, "`sys_user`", uId)
- })
- cfg := testutil.GetTestConfig()
- refreshToken, err := authHelper.GenerateRefreshToken(cfg.Auth.RefreshSecret, cfg.Auth.RefreshExpire, uId, uid, 0)
- require.NoError(t, err)
- srv := NewPermServer(svcCtx)
- resp, err := srv.RefreshToken(ctx, &pb.RefreshTokenReq{
- RefreshToken: refreshToken,
- ProductCode: uid,
- })
- require.NoError(t, err)
- assert.NotEmpty(t, resp.AccessToken)
- assert.NotEqual(t, refreshToken, resp.RefreshToken, "M-2修复:refreshToken必须发生轮转")
- newClaims, perr := authHelper.ParseRefreshToken(resp.RefreshToken, cfg.Auth.RefreshSecret)
- require.NoError(t, perr)
- assert.Equal(t, int64(1), newClaims.TokenVersion, "新 refreshToken 必须携带递增后的 tokenVersion")
- }
- // TC-0248: 普通用户+productCode
- func TestRefreshToken_NormalUserWithProductCode(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
- Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
- Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- uId, _ := uRes.LastInsertId()
- pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
- Code: uid, Name: "test_prod", AppKey: uid + "_k", AppSecret: "s1",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- mbrRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
- ProductCode: uid, UserId: uId, MemberType: "MEMBER",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- mbrId, _ := mbrRes.LastInsertId()
- pm1Res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: uid, Name: "p1", Code: uid + "_c1",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pm1Id, _ := pm1Res.LastInsertId()
- roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
- ProductCode: uid, Name: uid + "_role", Status: 1, PermsLevel: 1,
- CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- roleId, _ := roleRes.LastInsertId()
- urRes, err := svcCtx.SysUserRoleModel.Insert(ctx, &userRoleModel.SysUserRole{
- UserId: uId, RoleId: roleId, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- urId, _ := urRes.LastInsertId()
- rpRes, err := svcCtx.SysRolePermModel.Insert(ctx, &rolePermModel.SysRolePerm{
- RoleId: roleId, PermId: pm1Id, 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_user_role`", urId)
- testutil.CleanTable(ctx, conn, "`sys_perm`", pm1Id)
- testutil.CleanTable(ctx, conn, "`sys_role`", roleId)
- testutil.CleanTable(ctx, conn, "`sys_product_member`", mbrId)
- testutil.CleanTable(ctx, conn, "`sys_product`", pId)
- testutil.CleanTable(ctx, conn, "`sys_user`", uId)
- })
- cfg := testutil.GetTestConfig()
- refreshToken, err := authHelper.GenerateRefreshToken(cfg.Auth.RefreshSecret, cfg.Auth.RefreshExpire, uId, uid, 0)
- require.NoError(t, err)
- srv := NewPermServer(svcCtx)
- resp, err := srv.RefreshToken(ctx, &pb.RefreshTokenReq{
- RefreshToken: refreshToken,
- ProductCode: uid,
- })
- require.NoError(t, err)
- assert.NotEmpty(t, resp.AccessToken)
- assert.NotEqual(t, refreshToken, resp.RefreshToken, "M-2修复:refreshToken必须发生轮转")
- newClaims, perr := authHelper.ParseRefreshToken(resp.RefreshToken, cfg.Auth.RefreshSecret)
- require.NoError(t, perr)
- assert.Equal(t, int64(1), newClaims.TokenVersion, "新 refreshToken 必须携带递增后的 tokenVersion")
- }
- // ---------- VerifyToken ----------
- // TC-0249: 有效token(VerifyToken 现在实时查询DB,需要真实数据)
- func TestVerifyToken_Valid(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- cfg := testutil.GetTestConfig()
- conn := testutil.GetTestSqlConn()
- ts := time.Now().Unix()
- uid := testutil.UniqueId()
- uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
- Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick_verify",
- Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
- Status: 1, CreateTime: ts, UpdateTime: ts,
- })
- require.NoError(t, err)
- uId, _ := uRes.LastInsertId()
- pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
- Code: uid, Name: "prod_verify", AppKey: uid + "_k", AppSecret: "s1",
- Status: 1, CreateTime: ts, UpdateTime: ts,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- pmRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
- ProductCode: uid, UserId: uId, MemberType: "ADMIN", Status: 1,
- CreateTime: ts, UpdateTime: ts,
- })
- require.NoError(t, err)
- pmId, _ := pmRes.LastInsertId()
- pm1Res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: uid, Name: "perm_a", Code: "perm_a",
- Status: 1, CreateTime: ts, UpdateTime: ts,
- })
- require.NoError(t, err)
- pm1Id, _ := pm1Res.LastInsertId()
- pm2Res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: uid, Name: "perm_b", Code: "perm_b",
- Status: 1, CreateTime: ts, UpdateTime: ts,
- })
- require.NoError(t, err)
- pm2Id, _ := pm2Res.LastInsertId()
- t.Cleanup(func() {
- svcCtx.UserDetailsLoader.Del(ctx, uId, uid)
- testutil.CleanTable(ctx, conn, "`sys_perm`", pm1Id, pm2Id)
- testutil.CleanTable(ctx, conn, "`sys_product_member`", pmId)
- testutil.CleanTable(ctx, conn, "`sys_product`", pId)
- testutil.CleanTable(ctx, conn, "`sys_user`", uId)
- })
- svcCtx.UserDetailsLoader.Del(ctx, uId, uid)
- accessToken, err := authHelper.GenerateAccessToken(
- cfg.Auth.AccessSecret, cfg.Auth.AccessExpire,
- uId, uid, uid, "ADMIN", 0,
- )
- require.NoError(t, err)
- srv := NewPermServer(svcCtx)
- resp, err := srv.VerifyToken(ctx, &pb.VerifyTokenReq{AccessToken: accessToken})
- require.NoError(t, err)
- assert.True(t, resp.Valid)
- assert.Equal(t, uId, resp.UserId)
- assert.Equal(t, uid, resp.Username)
- assert.Equal(t, "ADMIN", resp.MemberType)
- assert.ElementsMatch(t, []string{"perm_a", "perm_b"}, resp.Perms)
- // BUG-02: proto定义了productCode字段,实现应返回产品编码
- assert.Equal(t, uid, resp.ProductCode, "BUG-02: VerifyTokenResp.ProductCode 应返回产品编码而非空字符串")
- }
- // TC-0250: 无效token
- func TestVerifyToken_Invalid(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- srv := NewPermServer(svcCtx)
- resp, err := srv.VerifyToken(ctx, &pb.VerifyTokenReq{AccessToken: "invalid.token.here"})
- require.NoError(t, err)
- assert.False(t, resp.Valid)
- }
- // TC-0251: 缺少userId
- func TestVerifyToken_MissingUserId(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- cfg := testutil.GetTestConfig()
- // Generate a token without userId by using raw JWT
- token := createTokenWithoutUserId(cfg.Auth.AccessSecret)
- srv := NewPermServer(svcCtx)
- resp, err := srv.VerifyToken(ctx, &pb.VerifyTokenReq{AccessToken: token})
- require.NoError(t, err)
- assert.False(t, resp.Valid)
- }
- // ---------- GetUserPerms ----------
- // TC-0255: 用户不存在
- func TestGetUserPerms_UserNotFound(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
- Code: uid, Name: "test_prod", AppKey: uid, AppSecret: bcryptHash(t, "secret1"),
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_product`", pId) })
- srv := NewPermServer(svcCtx)
- _, err = srv.GetUserPerms(ctx, &pb.GetUserPermsReq{
- UserId: 999999999,
- ProductCode: uid,
- AppKey: uid,
- AppSecret: "secret1",
- })
- require.Error(t, err)
- assert.Equal(t, codes.NotFound, status.Code(err))
- assert.Equal(t, "用户不存在", status.Convert(err).Message())
- }
- // TC-0256: 超管
- func TestGetUserPerms_SuperAdmin(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
- Username: uid, Password: testutil.HashPassword("pass"), Nickname: "sa",
- Avatar: sql.NullString{}, IsSuperAdmin: 1, MustChangePassword: 2,
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- uId, _ := uRes.LastInsertId()
- pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
- Code: uid, Name: "test_prod", AppKey: uid, AppSecret: bcryptHash(t, "secret1"),
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- pm1Res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: uid, Name: "p1", Code: uid + "_c1",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pm1Id, _ := pm1Res.LastInsertId()
- mRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
- ProductCode: uid, UserId: uId, MemberType: "ADMIN",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- mId, _ := mRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_product_member`", mId)
- testutil.CleanTable(ctx, conn, "`sys_perm`", pm1Id)
- testutil.CleanTable(ctx, conn, "`sys_product`", pId)
- testutil.CleanTable(ctx, conn, "`sys_user`", uId)
- })
- srv := NewPermServer(svcCtx)
- resp, err := srv.GetUserPerms(ctx, &pb.GetUserPermsReq{
- UserId: uId,
- ProductCode: uid,
- AppKey: uid,
- AppSecret: "secret1",
- })
- require.NoError(t, err)
- assert.Equal(t, "SUPER_ADMIN", resp.MemberType)
- assert.Contains(t, resp.Perms, uid+"_c1")
- }
- // TC-0234: 验证disabled计数
- func TestSyncPermissions_VerifyDisabledCount(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
- Code: uid, Name: "test_prod", AppKey: uid, AppSecret: bcryptHash(t, "secret1"),
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- var permIds []int64
- for i := 0; i < 5; i++ {
- pmRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: uid, Name: "p", Code: fmt.Sprintf("%s_c%d", uid, i),
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pmId, _ := pmRes.LastInsertId()
- permIds = append(permIds, pmId)
- }
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_perm`", permIds...)
- testutil.CleanTable(ctx, conn, "`sys_product`", pId)
- })
- srv := NewPermServer(svcCtx)
- resp, err := srv.SyncPermissions(ctx, &pb.SyncPermissionsReq{
- AppKey: uid,
- AppSecret: "secret1",
- Perms: []*pb.PermItem{
- {Code: fmt.Sprintf("%s_c0", uid), Name: "p"},
- {Code: fmt.Sprintf("%s_c1", uid), Name: "p"},
- },
- })
- require.NoError(t, err)
- assert.Equal(t, int64(3), resp.Disabled)
- }
- // TC-0257: MEMBER-DENY覆盖
- func TestGetUserPerms_MemberDENYOverride(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
- Username: uid, Password: testutil.HashPassword("pass"), Nickname: "nick",
- Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- uId, _ := uRes.LastInsertId()
- pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
- Code: uid, Name: "test_prod", AppKey: uid + "_k", AppSecret: bcryptHash(t, "secret1"),
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- mbrRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
- ProductCode: uid, UserId: uId, MemberType: "MEMBER",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- mbrId, _ := mbrRes.LastInsertId()
- roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
- ProductCode: uid, Name: uid + "_role", Status: 1, PermsLevel: 1,
- CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- roleId, _ := roleRes.LastInsertId()
- permARes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: uid, Name: "permA", Code: uid + "_pA",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- permAId, _ := permARes.LastInsertId()
- permBRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: uid, Name: "permB", Code: uid + "_pB",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- permBId, _ := permBRes.LastInsertId()
- urRes, err := svcCtx.SysUserRoleModel.Insert(ctx, &userRoleModel.SysUserRole{
- UserId: uId, RoleId: roleId, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- urId, _ := urRes.LastInsertId()
- rpARes, err := svcCtx.SysRolePermModel.Insert(ctx, &rolePermModel.SysRolePerm{
- RoleId: roleId, PermId: permAId, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- rpAId, _ := rpARes.LastInsertId()
- rpBRes, err := svcCtx.SysRolePermModel.Insert(ctx, &rolePermModel.SysRolePerm{
- RoleId: roleId, PermId: permBId, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- rpBId, _ := rpBRes.LastInsertId()
- upRes, err := svcCtx.SysUserPermModel.Insert(ctx, &userPermModel.SysUserPerm{
- UserId: uId, PermId: permAId, Effect: "DENY",
- CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- upId, _ := upRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_user_perm`", upId)
- testutil.CleanTable(ctx, conn, "`sys_role_perm`", rpAId, rpBId)
- testutil.CleanTable(ctx, conn, "`sys_user_role`", urId)
- testutil.CleanTable(ctx, conn, "`sys_perm`", permAId, permBId)
- testutil.CleanTable(ctx, conn, "`sys_role`", roleId)
- testutil.CleanTable(ctx, conn, "`sys_product_member`", mbrId)
- testutil.CleanTable(ctx, conn, "`sys_product`", pId)
- testutil.CleanTable(ctx, conn, "`sys_user`", uId)
- })
- srv := NewPermServer(svcCtx)
- resp, err := srv.GetUserPerms(ctx, &pb.GetUserPermsReq{
- UserId: uId,
- ProductCode: uid,
- AppKey: uid + "_k",
- AppSecret: "secret1",
- })
- require.NoError(t, err)
- assert.Equal(t, "MEMBER", resp.MemberType)
- assert.Contains(t, resp.Perms, uid+"_pB")
- assert.NotContains(t, resp.Perms, uid+"_pA")
- }
- // TC-0252: gRPC VerifyToken 用户已冻结返回valid=false(H-4修复验证)
- func TestVerifyToken_FrozenUserReturnsInvalid(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- cfg := testutil.GetTestConfig()
- uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
- Username: uid, Password: testutil.HashPassword("pass"), Nickname: "frozen",
- Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
- Status: 2, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- uId, _ := uRes.LastInsertId()
- t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", uId) })
- accessToken, err := authHelper.GenerateAccessToken(
- cfg.Auth.AccessSecret, cfg.Auth.AccessExpire,
- uId, uid, "", "MEMBER", 0,
- )
- require.NoError(t, err)
- srv := NewPermServer(svcCtx)
- resp, err := srv.VerifyToken(ctx, &pb.VerifyTokenReq{AccessToken: accessToken})
- require.NoError(t, err)
- assert.False(t, resp.Valid, "frozen user token should be invalid")
- }
- // TC-0253: gRPC VerifyToken 非产品成员返回valid=false(H-4修复验证)
- func TestVerifyToken_NonMemberReturnsInvalid(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- pc := testutil.UniqueId()
- cfg := testutil.GetTestConfig()
- uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
- Username: uid, Password: testutil.HashPassword("pass"), Nickname: "user",
- Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- uId, _ := uRes.LastInsertId()
- pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
- Code: pc, Name: "prod", AppKey: testutil.UniqueId(), AppSecret: "s",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_product`", pId)
- testutil.CleanTable(ctx, conn, "`sys_user`", uId)
- })
- accessToken, err := authHelper.GenerateAccessToken(
- cfg.Auth.AccessSecret, cfg.Auth.AccessExpire,
- uId, uid, pc, "MEMBER", 0,
- )
- require.NoError(t, err)
- srv := NewPermServer(svcCtx)
- resp, err := srv.VerifyToken(ctx, &pb.VerifyTokenReq{AccessToken: accessToken})
- require.NoError(t, err)
- assert.False(t, resp.Valid, "non-member user with productCode should be invalid")
- }
- // TC-0254: gRPC VerifyToken 返回实时权限和成员类型(H-4修复验证)
- func TestVerifyToken_ReturnsRealtimeData(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- cfg := testutil.GetTestConfig()
- uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
- Username: uid, Password: testutil.HashPassword("pass"), Nickname: "user",
- Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- uId, _ := uRes.LastInsertId()
- pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
- Code: uid, Name: "prod", AppKey: uid + "_k", AppSecret: "s",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- mbrRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
- ProductCode: uid, UserId: uId, MemberType: "ADMIN",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- mbrId, _ := mbrRes.LastInsertId()
- permRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: uid, Name: "realtime_perm", Code: uid + "_rt",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- permId, _ := permRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_perm`", permId)
- testutil.CleanTable(ctx, conn, "`sys_product_member`", mbrId)
- testutil.CleanTable(ctx, conn, "`sys_product`", pId)
- testutil.CleanTable(ctx, conn, "`sys_user`", uId)
- })
- accessToken, err := authHelper.GenerateAccessToken(
- cfg.Auth.AccessSecret, cfg.Auth.AccessExpire,
- uId, uid, uid, "MEMBER", 0,
- )
- require.NoError(t, err)
- svcCtx.UserDetailsLoader.Clean(ctx, uId)
- srv := NewPermServer(svcCtx)
- resp, err := srv.VerifyToken(ctx, &pb.VerifyTokenReq{AccessToken: accessToken})
- require.NoError(t, err)
- assert.True(t, resp.Valid)
- assert.Equal(t, "ADMIN", resp.MemberType, "should return realtime memberType, not token's")
- assert.Contains(t, resp.Perms, uid+"_rt", "should return realtime perms")
- }
- // TC-0241: gRPC Login 产品成员被禁用时拒绝(H-3修复验证)
- func TestLogin_DisabledMemberRejected(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
- Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick",
- Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- uId, _ := uRes.LastInsertId()
- pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
- Code: uid, Name: "prod", AppKey: uid + "_k", AppSecret: "s1",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- pmRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
- ProductCode: uid, UserId: uId, MemberType: "MEMBER", Status: 2,
- CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pmId, _ := pmRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_product_member`", pmId)
- testutil.CleanTable(ctx, conn, "`sys_product`", pId)
- testutil.CleanTable(ctx, conn, "`sys_user`", uId)
- })
- srv := NewPermServer(svcCtx)
- _, err = srv.Login(ctx, &pb.LoginReq{
- Username: uid,
- Password: "pass123",
- ProductCode: uid,
- })
- require.Error(t, err)
- assert.Equal(t, codes.PermissionDenied, status.Code(err))
- assert.Equal(t, "您在该产品下的成员资格已被禁用", status.Convert(err).Message())
- }
- // helper: create a JWT with no userId claim
- func createTokenWithoutUserId(secret string) string {
- claims := jwt.MapClaims{
- "username": "test",
- "exp": time.Now().Add(time.Hour).Unix(),
- }
- token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
- s, _ := token.SignedString([]byte(secret))
- return s
- }
- // =============================================================================
- // audit H-2 修复回归测试:gRPC GetUserPerms 必须对齐 VerifyToken 的状态校验
- // 修复前:GetUserPerms 仅校验"用户存在";冻结用户/被踢出产品的用户仍会被返回全量权限。
- // 修复后:增加 StatusEnabled 判定 + (非超管下)MemberType 非空判定。
- // =============================================================================
- // TC-0700: GetUserPerms 对冻结用户 (Status=Disabled) 必须返回 PermissionDenied
- func TestGetUserPerms_FrozenUser_PermissionDenied(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- // 用户 Status=2 (Disabled)
- uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
- Username: uid, Password: testutil.HashPassword("pass"), Nickname: "frozen",
- Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
- Status: 2, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- uId, _ := uRes.LastInsertId()
- pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
- Code: uid, Name: "prod", AppKey: uid + "_k", AppSecret: bcryptHash(t, "s"),
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- // 插入该产品下启用成员,保证 MemberType != "",排除冻结用户与非成员两个判定路径的干扰
- mRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
- ProductCode: uid, UserId: uId, MemberType: "MEMBER", Status: 1,
- CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- mId, _ := mRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_product_member`", mId)
- testutil.CleanTable(ctx, conn, "`sys_product`", pId)
- testutil.CleanTable(ctx, conn, "`sys_user`", uId)
- })
- // 清理缓存确保 loader 从 DB 取最新的 Status=2
- svcCtx.UserDetailsLoader.Clean(ctx, uId)
- srv := NewPermServer(svcCtx)
- _, err = srv.GetUserPerms(ctx, &pb.GetUserPermsReq{
- UserId: uId, ProductCode: uid, AppKey: uid + "_k", AppSecret: "s",
- })
- require.Error(t, err, "冻结用户的 GetUserPerms 必须返回错误,不能再返回全量权限")
- assert.Equal(t, codes.PermissionDenied, status.Code(err),
- "audit H-2: 冻结用户应返回 PermissionDenied 以阻断跨系统一致性漏洞")
- assert.Contains(t, status.Convert(err).Message(), "冻结")
- }
- // TC-0701: GetUserPerms 对已被移出产品的启用用户(非超管 + MemberType 空)必须返回 PermissionDenied
- func TestGetUserPerms_NonMember_PermissionDenied(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- // 用户启用但不是目标产品的成员
- uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
- Username: uid, Password: testutil.HashPassword("pass"), Nickname: "non_member",
- Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- uId, _ := uRes.LastInsertId()
- pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
- Code: uid, Name: "prod", AppKey: uid + "_k", AppSecret: bcryptHash(t, "s"),
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_product`", pId)
- testutil.CleanTable(ctx, conn, "`sys_user`", uId)
- })
- svcCtx.UserDetailsLoader.Clean(ctx, uId)
- srv := NewPermServer(svcCtx)
- _, err = srv.GetUserPerms(ctx, &pb.GetUserPermsReq{
- UserId: uId, ProductCode: uid, AppKey: uid + "_k", AppSecret: "s",
- })
- require.Error(t, err)
- assert.Equal(t, codes.PermissionDenied, status.Code(err),
- "audit H-2: 用户不是产品成员时应返回 PermissionDenied")
- assert.Contains(t, status.Convert(err).Message(), "成员")
- }
- // TC-0702: GetUserPerms 对"产品成员被禁用的 DEV 部门用户"必须返回 PermissionDenied
- // 组合 H-2 + H-3 的交叉场景:禁用成员 → MemberType 清空 → 即便 DeptType=DEV 也不应获得权限
- func TestGetUserPerms_DisabledMemberInDevDept_PermissionDenied(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- // 插入 DEV 部门
- deptRes, err := svcCtx.SysDeptModel.Insert(ctx, &deptModel.SysDept{
- Name: "dev_" + uid, ParentId: 0, Path: "/",
- DeptType: "DEV", Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- deptId, _ := deptRes.LastInsertId()
- uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
- Username: uid, Password: testutil.HashPassword("pass"), Nickname: "dev_user",
- Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2, DeptId: deptId,
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- uId, _ := uRes.LastInsertId()
- pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
- Code: uid, Name: "prod", AppKey: uid + "_k", AppSecret: bcryptHash(t, "s"),
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- // 被管理员禁用的产品成员 (Status=2)
- mRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
- ProductCode: uid, UserId: uId, MemberType: "MEMBER", Status: 2,
- CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- mId, _ := mRes.LastInsertId()
- // 放几条启用权限,验证"本来能拿到"
- permRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: uid, Name: "all", Code: uid + "_all",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- permId, _ := permRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_perm`", permId)
- testutil.CleanTable(ctx, conn, "`sys_product_member`", mId)
- testutil.CleanTable(ctx, conn, "`sys_product`", pId)
- testutil.CleanTable(ctx, conn, "`sys_user`", uId)
- testutil.CleanTable(ctx, conn, "`sys_dept`", deptId)
- })
- svcCtx.UserDetailsLoader.Clean(ctx, uId)
- srv := NewPermServer(svcCtx)
- _, err = srv.GetUserPerms(ctx, &pb.GetUserPermsReq{
- UserId: uId, ProductCode: uid, AppKey: uid + "_k", AppSecret: "s",
- })
- require.Error(t, err,
- "audit H-3: 产品成员被禁用的 DEV 部门用户不应再被 loadPerms 授予全量权限,"+
- "GetUserPerms 也不应返回 PermissionDenied 以外的结果")
- assert.Equal(t, codes.PermissionDenied, status.Code(err))
- }
- // TC-0703: GetUserPerms 对"启用的产品成员"返回成功(H-2 回归基准)
- // 验证修复后的正常路径未被误伤
- func TestGetUserPerms_EnabledMember_Succeeds(t *testing.T) {
- ctx := context.Background()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- uid := testutil.UniqueId()
- uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
- Username: uid, Password: testutil.HashPassword("pass"), Nickname: "ok",
- Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2,
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- uId, _ := uRes.LastInsertId()
- pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
- Code: uid, Name: "prod", AppKey: uid + "_k", AppSecret: bcryptHash(t, "s"),
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- mRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{
- ProductCode: uid, UserId: uId, MemberType: "ADMIN", Status: 1,
- CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- mId, _ := mRes.LastInsertId()
- permRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: uid, Name: "p", Code: uid + "_c",
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- permId, _ := permRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_perm`", permId)
- testutil.CleanTable(ctx, conn, "`sys_product_member`", mId)
- testutil.CleanTable(ctx, conn, "`sys_product`", pId)
- testutil.CleanTable(ctx, conn, "`sys_user`", uId)
- })
- srv := NewPermServer(svcCtx)
- resp, err := srv.GetUserPerms(ctx, &pb.GetUserPermsReq{
- UserId: uId, ProductCode: uid, AppKey: uid + "_k", AppSecret: "s",
- })
- require.NoError(t, err)
- assert.Equal(t, "ADMIN", resp.MemberType)
- assert.Contains(t, resp.Perms, uid+"_c")
- }
|