| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381 |
- package pub
- import (
- "context"
- "database/sql"
- "errors"
- "testing"
- "time"
- authHelper "perms-system-server/internal/logic/auth"
- permModel "perms-system-server/internal/model/perm"
- productmemberModel "perms-system-server/internal/model/productmember"
- userModel "perms-system-server/internal/model/user"
- "perms-system-server/internal/response"
- "perms-system-server/internal/testutil"
- "perms-system-server/internal/types"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- )
- func insertRefreshTestUser(t *testing.T, ctx context.Context, username, password string, status, isSuperAdmin int64) (int64, func()) {
- t.Helper()
- svcCtx := newTestSvcCtx()
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- hashed := testutil.HashPassword(password)
- res, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
- Username: username,
- Password: hashed,
- Nickname: username,
- Avatar: sql.NullString{},
- Email: username + "@test.com",
- Phone: "13800000000",
- Remark: "",
- DeptId: 0,
- IsSuperAdmin: isSuperAdmin,
- MustChangePassword: 2,
- Status: status,
- CreateTime: now,
- UpdateTime: now,
- })
- require.NoError(t, err)
- id, _ := res.LastInsertId()
- cleanup := func() {
- testutil.CleanTable(ctx, conn, "`sys_user`", id)
- }
- return id, cleanup
- }
- // TC-0026: 正常刷新(refreshToken从header获取,原样返回不重新生成)
- func TestRefreshToken_Normal(t *testing.T) {
- ctx := context.Background()
- svcCtx := newTestSvcCtx()
- username := testutil.UniqueId()
- password := "TestPass123"
- userId, cleanUser := insertRefreshTestUser(t, ctx, username, password, 1, 2)
- t.Cleanup(cleanUser)
- refreshToken, err := authHelper.GenerateRefreshToken(
- svcCtx.Config.Auth.RefreshSecret,
- svcCtx.Config.Auth.RefreshExpire,
- userId, "", 0,
- )
- require.NoError(t, err)
- logic := NewRefreshTokenLogic(ctx, svcCtx)
- resp, err := logic.RefreshToken(&types.RefreshTokenReq{
- Authorization: "Bearer " + refreshToken,
- })
- require.NoError(t, err)
- require.NotNil(t, resp)
- assert.NotEmpty(t, resp.AccessToken)
- assert.NotEmpty(t, resp.RefreshToken, "应返回新的refreshToken")
- assert.NotEqual(t, resp.AccessToken, resp.RefreshToken, "accessToken和refreshToken应不同")
- assert.True(t, resp.Expires > time.Now().Unix(), "expires应为未来的unix时间戳")
- assert.Equal(t, userId, resp.UserInfo.UserId)
- assert.Equal(t, username, resp.UserInfo.Username)
- }
- // TC-0027: 不带productCode(回退)
- func TestRefreshToken_FallbackToClaimsProductCode(t *testing.T) {
- ctx := context.Background()
- svcCtx := newTestSvcCtx()
- conn := testutil.GetTestSqlConn()
- username := testutil.UniqueId()
- password := "TestPass123"
- pc := testutil.UniqueId()
- now := time.Now().Unix()
- userId, cleanUser := insertRefreshTestUser(t, ctx, username, password, 1, 2)
- t.Cleanup(cleanUser)
- _, cleanProduct := insertTestProduct(t, ctx, svcCtx, pc, testutil.UniqueId(), "secret")
- t.Cleanup(cleanProduct)
- pmRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &productmemberModel.SysProductMember{
- ProductCode: pc, UserId: userId, MemberType: "ADMIN", Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pmId, _ := pmRes.LastInsertId()
- t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_product_member`", pmId) })
- permCode := testutil.UniqueId()
- permRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: pc, Name: "refresh_perm", Code: permCode, Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- permId, _ := permRes.LastInsertId()
- t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_perm`", permId) })
- refreshToken, err := authHelper.GenerateRefreshToken(
- svcCtx.Config.Auth.RefreshSecret,
- svcCtx.Config.Auth.RefreshExpire,
- userId, pc, 0,
- )
- require.NoError(t, err)
- logic := NewRefreshTokenLogic(ctx, svcCtx)
- resp, err := logic.RefreshToken(&types.RefreshTokenReq{
- Authorization: "Bearer " + refreshToken,
- })
- require.NoError(t, err)
- require.NotNil(t, resp)
- assert.Equal(t, "ADMIN", resp.UserInfo.MemberType)
- assert.Contains(t, resp.UserInfo.Perms, permCode)
- }
- // TC-0028: token无效
- func TestRefreshToken_InvalidToken(t *testing.T) {
- ctx := context.Background()
- svcCtx := newTestSvcCtx()
- logic := NewRefreshTokenLogic(ctx, svcCtx)
- resp, err := logic.RefreshToken(&types.RefreshTokenReq{
- Authorization: "Bearer invalid.token.string",
- })
- require.Nil(t, resp)
- require.Error(t, err)
- var codeErr *response.CodeError
- require.True(t, errors.As(err, &codeErr))
- assert.Equal(t, 401, codeErr.Code())
- assert.Equal(t, "refreshToken无效或已过期", codeErr.Error())
- }
- // TC-0029: 用户已删除(UserDetailsLoader 返回 Status=0 → 403 账号已被冻结)
- func TestRefreshToken_UserDeleted(t *testing.T) {
- ctx := context.Background()
- svcCtx := newTestSvcCtx()
- nonExistentUserId := int64(999999999)
- refreshToken, err := authHelper.GenerateRefreshToken(
- svcCtx.Config.Auth.RefreshSecret,
- svcCtx.Config.Auth.RefreshExpire,
- nonExistentUserId, "", 0,
- )
- require.NoError(t, err)
- logic := NewRefreshTokenLogic(ctx, svcCtx)
- resp, err := logic.RefreshToken(&types.RefreshTokenReq{
- Authorization: "Bearer " + refreshToken,
- })
- require.Nil(t, resp)
- require.Error(t, err)
- var codeErr *response.CodeError
- require.True(t, errors.As(err, &codeErr))
- assert.Equal(t, 403, codeErr.Code())
- assert.Equal(t, "账号已被冻结", codeErr.Error())
- }
- // TC-0030: 账号冻结
- func TestRefreshToken_AccountFrozen(t *testing.T) {
- ctx := context.Background()
- svcCtx := newTestSvcCtx()
- username := testutil.UniqueId()
- password := "TestPass123"
- userId, cleanUser := insertRefreshTestUser(t, ctx, username, password, 2, 2)
- t.Cleanup(cleanUser)
- refreshToken, err := authHelper.GenerateRefreshToken(
- svcCtx.Config.Auth.RefreshSecret,
- svcCtx.Config.Auth.RefreshExpire,
- userId, "", 0,
- )
- require.NoError(t, err)
- logic := NewRefreshTokenLogic(ctx, svcCtx)
- resp, err := logic.RefreshToken(&types.RefreshTokenReq{
- Authorization: "Bearer " + refreshToken,
- })
- require.Nil(t, resp)
- require.Error(t, err)
- var codeErr *response.CodeError
- require.True(t, errors.As(err, &codeErr))
- assert.Equal(t, 403, codeErr.Code())
- assert.Equal(t, "账号已被冻结", codeErr.Error())
- }
- // TC-0032: 尝试切换产品被拒绝
- func TestRefreshToken_ProductCodeSwitchRejected(t *testing.T) {
- ctx := context.Background()
- svcCtx := newTestSvcCtx()
- username := testutil.UniqueId()
- password := "TestPass123"
- userId, cleanUser := insertRefreshTestUser(t, ctx, username, password, 1, 2)
- t.Cleanup(cleanUser)
- refreshToken, err := authHelper.GenerateRefreshToken(
- svcCtx.Config.Auth.RefreshSecret,
- svcCtx.Config.Auth.RefreshExpire,
- userId, "product_a", 0,
- )
- require.NoError(t, err)
- logic := NewRefreshTokenLogic(ctx, svcCtx)
- resp, err := logic.RefreshToken(&types.RefreshTokenReq{
- Authorization: "Bearer " + refreshToken,
- ProductCode: "product_b",
- })
- require.Nil(t, resp)
- require.Error(t, err)
- var codeErr *response.CodeError
- require.True(t, errors.As(err, &codeErr))
- assert.Equal(t, 400, codeErr.Code())
- assert.Equal(t, "刷新令牌不允许切换产品", codeErr.Error())
- }
- // TC-0033: TokenVersion不匹配时拒绝刷新
- func TestRefreshToken_TokenVersionMismatch(t *testing.T) {
- ctx := context.Background()
- svcCtx := newTestSvcCtx()
- username := testutil.UniqueId()
- password := "TestPass123"
- userId, cleanUser := insertRefreshTestUser(t, ctx, username, password, 1, 2)
- t.Cleanup(cleanUser)
- refreshToken, err := authHelper.GenerateRefreshToken(
- svcCtx.Config.Auth.RefreshSecret,
- svcCtx.Config.Auth.RefreshExpire,
- userId, "", 999,
- )
- require.NoError(t, err)
- logic := NewRefreshTokenLogic(ctx, svcCtx)
- resp, err := logic.RefreshToken(&types.RefreshTokenReq{
- Authorization: "Bearer " + refreshToken,
- })
- require.Nil(t, resp)
- require.Error(t, err)
- var codeErr *response.CodeError
- require.True(t, errors.As(err, &codeErr))
- assert.Equal(t, 401, codeErr.Code())
- assert.Equal(t, "登录状态已失效,请重新登录", codeErr.Error())
- }
- // TC-0034: 使用accessToken作为refreshToken被拒绝
- func TestRefreshToken_AccessTokenRejected(t *testing.T) {
- ctx := context.Background()
- svcCtx := newTestSvcCtx()
- username := testutil.UniqueId()
- password := "TestPass123"
- userId, cleanUser := insertRefreshTestUser(t, ctx, username, password, 1, 2)
- t.Cleanup(cleanUser)
- accessToken, err := authHelper.GenerateAccessToken(
- svcCtx.Config.Auth.RefreshSecret,
- svcCtx.Config.Auth.AccessExpire,
- userId, username, "", "", 0,
- )
- require.NoError(t, err)
- logic := NewRefreshTokenLogic(ctx, svcCtx)
- resp, err := logic.RefreshToken(&types.RefreshTokenReq{
- Authorization: "Bearer " + accessToken,
- })
- require.Nil(t, resp)
- require.Error(t, err)
- var codeErr *response.CodeError
- require.True(t, errors.As(err, &codeErr))
- assert.Equal(t, 401, codeErr.Code())
- assert.Equal(t, "refreshToken无效或已过期", codeErr.Error())
- }
- // TC-0035: 产品成员已移除时拒绝刷新
- func TestRefreshToken_MemberRemovedRejected(t *testing.T) {
- ctx := context.Background()
- svcCtx := newTestSvcCtx()
- conn := testutil.GetTestSqlConn()
- username := testutil.UniqueId()
- password := "TestPass123"
- pc := testutil.UniqueId()
- now := time.Now().Unix()
- userId, cleanUser := insertRefreshTestUser(t, ctx, username, password, 1, 2)
- t.Cleanup(cleanUser)
- _, cleanProduct := insertTestProduct(t, ctx, svcCtx, pc, testutil.UniqueId(), "secret")
- t.Cleanup(cleanProduct)
- pmRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &productmemberModel.SysProductMember{
- ProductCode: pc, UserId: userId, MemberType: "MEMBER", Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pmId, _ := pmRes.LastInsertId()
- refreshToken, err := authHelper.GenerateRefreshToken(
- svcCtx.Config.Auth.RefreshSecret,
- svcCtx.Config.Auth.RefreshExpire,
- userId, pc, 0,
- )
- require.NoError(t, err)
- testutil.CleanTable(ctx, conn, "`sys_product_member`", pmId)
- logic := NewRefreshTokenLogic(ctx, svcCtx)
- resp, err := logic.RefreshToken(&types.RefreshTokenReq{
- Authorization: "Bearer " + refreshToken,
- })
- require.Nil(t, resp)
- require.Error(t, err)
- var codeErr *response.CodeError
- require.True(t, errors.As(err, &codeErr))
- assert.Equal(t, 403, codeErr.Code())
- assert.Equal(t, "您已不是该产品的成员", codeErr.Error())
- }
- // TC-0031: 超管+productCode(refreshToken原样返回)
- func TestRefreshToken_SuperAdminWithProductCode(t *testing.T) {
- ctx := context.Background()
- svcCtx := newTestSvcCtx()
- conn := testutil.GetTestSqlConn()
- username := testutil.UniqueId()
- password := "TestPass123"
- pc := testutil.UniqueId()
- now := time.Now().Unix()
- userId, cleanUser := insertRefreshTestUser(t, ctx, username, password, 1, 1)
- t.Cleanup(cleanUser)
- _, cleanProduct := insertTestProduct(t, ctx, svcCtx, pc, testutil.UniqueId(), "secret")
- t.Cleanup(cleanProduct)
- permCode := testutil.UniqueId()
- permRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: pc, Name: "sa_refresh_perm", Code: permCode, Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- permId, _ := permRes.LastInsertId()
- t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_perm`", permId) })
- refreshToken, err := authHelper.GenerateRefreshToken(
- svcCtx.Config.Auth.RefreshSecret,
- svcCtx.Config.Auth.RefreshExpire,
- userId, pc, 0,
- )
- require.NoError(t, err)
- logic := NewRefreshTokenLogic(ctx, svcCtx)
- resp, err := logic.RefreshToken(&types.RefreshTokenReq{
- Authorization: "Bearer " + refreshToken,
- ProductCode: pc,
- })
- require.NoError(t, err)
- require.NotNil(t, resp)
- assert.NotEmpty(t, resp.RefreshToken, "应返回新的refreshToken")
- assert.Equal(t, "SUPER_ADMIN", resp.UserInfo.MemberType)
- assert.Contains(t, resp.UserInfo.Perms, permCode)
- assert.Equal(t, int64(1), resp.UserInfo.IsSuperAdmin)
- }
|