| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- package pub
- import (
- "context"
- "database/sql"
- "errors"
- "testing"
- "time"
- permModel "perms-system-server/internal/model/perm"
- productModel "perms-system-server/internal/model/product"
- productmemberModel "perms-system-server/internal/model/productmember"
- userModel "perms-system-server/internal/model/user"
- "perms-system-server/internal/response"
- "perms-system-server/internal/svc"
- "perms-system-server/internal/testutil"
- "perms-system-server/internal/types"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- )
- func newTestSvcCtx() *svc.ServiceContext {
- return svc.NewServiceContext(testutil.GetTestConfig())
- }
- func insertTestUser(t *testing.T, ctx context.Context, svcCtx *svc.ServiceContext, username, password string, status, isSuperAdmin int64) (int64, func()) {
- t.Helper()
- 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
- }
- func insertTestProduct(t *testing.T, ctx context.Context, svcCtx *svc.ServiceContext, code, appKey, appSecret string) (int64, func()) {
- t.Helper()
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- res, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
- Code: code,
- Name: code,
- AppKey: appKey,
- AppSecret: appSecret,
- Status: 1,
- CreateTime: now,
- UpdateTime: now,
- })
- require.NoError(t, err)
- id, _ := res.LastInsertId()
- cleanup := func() {
- testutil.CleanTable(ctx, conn, "`sys_product`", id)
- }
- return id, cleanup
- }
- // TC-0001: 正常登录(普通用户+productCode)
- func TestLogin_NormalWithProductCodeBasic(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 := insertTestUser(t, ctx, svcCtx, 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()
- t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_product_member`", pmId) })
- logic := NewLoginLogic(ctx, svcCtx)
- resp, err := logic.Login(&types.LoginReq{
- Username: username,
- Password: password,
- ProductCode: pc,
- })
- require.NoError(t, err)
- require.NotNil(t, resp)
- assert.NotEmpty(t, resp.AccessToken)
- assert.NotEmpty(t, resp.RefreshToken)
- assert.True(t, resp.Expires > time.Now().Unix(), "expires应为未来的unix时间戳")
- assert.Equal(t, username, resp.UserInfo.Username)
- assert.Equal(t, "MEMBER", resp.UserInfo.MemberType)
- }
- // TC-0002: 正常登录-带productCode
- func TestLogin_NormalWithProductCode(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 := insertTestUser(t, ctx, svcCtx, 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) })
- permRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: pc, Name: "perm1", Code: testutil.UniqueId(), Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- permId, _ := permRes.LastInsertId()
- t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_perm`", permId) })
- logic := NewLoginLogic(ctx, svcCtx)
- resp, err := logic.Login(&types.LoginReq{
- Username: username,
- Password: password,
- ProductCode: pc,
- })
- require.NoError(t, err)
- require.NotNil(t, resp)
- assert.Equal(t, "ADMIN", resp.UserInfo.MemberType)
- assert.NotEmpty(t, resp.UserInfo.Perms)
- }
- // TC-0003: 超管通过产品端登录被拒绝
- func TestLogin_SuperAdminRejected(t *testing.T) {
- ctx := context.Background()
- svcCtx := newTestSvcCtx()
- username := testutil.UniqueId()
- password := "TestPass123"
- pc := testutil.UniqueId()
- _, cleanUser := insertTestUser(t, ctx, svcCtx, username, password, 1, 1)
- t.Cleanup(cleanUser)
- _, cleanProduct := insertTestProduct(t, ctx, svcCtx, pc, testutil.UniqueId(), "secret")
- t.Cleanup(cleanProduct)
- logic := NewLoginLogic(ctx, svcCtx)
- resp, err := logic.Login(&types.LoginReq{
- Username: username,
- Password: password,
- ProductCode: pc,
- })
- 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-0004: 超管无productCode被拒绝
- func TestLogin_SuperAdminWithoutProductCodeRejected(t *testing.T) {
- ctx := context.Background()
- svcCtx := newTestSvcCtx()
- username := testutil.UniqueId()
- password := "TestPass123"
- _, cleanUser := insertTestUser(t, ctx, svcCtx, username, password, 1, 1)
- t.Cleanup(cleanUser)
- logic := NewLoginLogic(ctx, svcCtx)
- resp, err := logic.Login(&types.LoginReq{
- Username: username,
- Password: password,
- })
- 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-0005: 用户不存在
- func TestLogin_UserNotFound(t *testing.T) {
- ctx := context.Background()
- svcCtx := newTestSvcCtx()
- logic := NewLoginLogic(ctx, svcCtx)
- resp, err := logic.Login(&types.LoginReq{
- Username: "nonexistent_" + testutil.UniqueId(),
- Password: "whatever",
- })
- 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-0007: 密码错误
- func TestLogin_WrongPassword(t *testing.T) {
- ctx := context.Background()
- svcCtx := newTestSvcCtx()
- username := testutil.UniqueId()
- _, cleanUser := insertTestUser(t, ctx, svcCtx, username, "CorrectPass", 1, 2)
- t.Cleanup(cleanUser)
- logic := NewLoginLogic(ctx, svcCtx)
- resp, err := logic.Login(&types.LoginReq{
- Username: username,
- Password: "WrongPass",
- })
- 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-0008: 账号冻结
- func TestLogin_AccountFrozen(t *testing.T) {
- ctx := context.Background()
- svcCtx := newTestSvcCtx()
- username := testutil.UniqueId()
- password := "TestPass123"
- _, cleanUser := insertTestUser(t, ctx, svcCtx, username, password, 2, 2)
- t.Cleanup(cleanUser)
- logic := NewLoginLogic(ctx, svcCtx)
- resp, err := logic.Login(&types.LoginReq{
- Username: username,
- Password: password,
- })
- 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-0009: 非产品成员
- func TestLogin_NonMemberWithProductCode(t *testing.T) {
- ctx := context.Background()
- svcCtx := newTestSvcCtx()
- username := testutil.UniqueId()
- password := "TestPass123"
- pc := testutil.UniqueId()
- _, cleanUser := insertTestUser(t, ctx, svcCtx, username, password, 1, 2)
- t.Cleanup(cleanUser)
- _, cleanProduct := insertTestProduct(t, ctx, svcCtx, pc, testutil.UniqueId(), "secret")
- t.Cleanup(cleanProduct)
- logic := NewLoginLogic(ctx, svcCtx)
- resp, err := logic.Login(&types.LoginReq{
- Username: username,
- Password: password,
- ProductCode: pc,
- })
- 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())
- // loginService 去除重复 FindOneByProductCodeUserId,所有非成员/禁用成员分支合并
- assert.Equal(t, "您不是该产品的有效成员", codeErr.Error())
- }
- // TC-0010: DEVELOPER成员
- func TestLogin_DeveloperMember(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 := insertTestUser(t, ctx, svcCtx, 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: "DEVELOPER", 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: "dev_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) })
- logic := NewLoginLogic(ctx, svcCtx)
- resp, err := logic.Login(&types.LoginReq{
- Username: username,
- Password: password,
- ProductCode: pc,
- })
- require.NoError(t, err)
- require.NotNil(t, resp)
- assert.Equal(t, "DEVELOPER", resp.UserInfo.MemberType)
- assert.Contains(t, resp.UserInfo.Perms, permCode)
- }
- // TC-0011: SQL注入
- func TestLogin_SQLInjection(t *testing.T) {
- ctx := context.Background()
- svcCtx := newTestSvcCtx()
- logic := NewLoginLogic(ctx, svcCtx)
- resp, err := logic.Login(&types.LoginReq{
- Username: "' OR 1=1 --",
- Password: "anything",
- })
- 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-0013: 产品成员被禁用时拒绝登录(修复验证)
- func TestLogin_DisabledMemberRejected(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 := insertTestUser(t, ctx, svcCtx, 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: 2, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pmId, _ := pmRes.LastInsertId()
- t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_product_member`", pmId) })
- logic := NewLoginLogic(ctx, svcCtx)
- resp, err := logic.Login(&types.LoginReq{
- Username: username,
- Password: password,
- ProductCode: pc,
- })
- require.Nil(t, resp)
- require.Error(t, err)
- var codeErr2 *response.CodeError
- require.True(t, errors.As(err, &codeErr2))
- assert.Equal(t, 403, codeErr2.Code())
- // 禁用成员在 loadMembership 阶段即被清空 MemberType,与"非成员"文案合并
- assert.Equal(t, "您不是该产品的有效成员", codeErr2.Error())
- }
- // TC-0014: 产品已被禁用时拒绝登录
- func TestLogin_DisabledProductRejected(t *testing.T) {
- ctx := context.Background()
- svcCtx := newTestSvcCtx()
- conn := testutil.GetTestSqlConn()
- username := testutil.UniqueId()
- password := "TestPass123"
- pc := testutil.UniqueId()
- now := time.Now().Unix()
- _, cleanUser := insertTestUser(t, ctx, svcCtx, username, password, 1, 2)
- t.Cleanup(cleanUser)
- pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{
- Code: pc, Name: pc, AppKey: testutil.UniqueId(), AppSecret: "secret",
- Status: 2, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_product`", pId) })
- logic := NewLoginLogic(ctx, svcCtx)
- resp, err := logic.Login(&types.LoginReq{
- Username: username,
- Password: password,
- ProductCode: pc,
- })
- require.Nil(t, resp)
- require.Error(t, err)
- var codeErr3 *response.CodeError
- require.True(t, errors.As(err, &codeErr3))
- assert.Equal(t, 403, codeErr3.Code())
- assert.Equal(t, "该产品已被禁用", codeErr3.Error())
- }
|