| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485 |
- package pub
- import (
- "context"
- "errors"
- "testing"
- "perms-system-server/internal/testutil"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "github.com/zeromicro/go-zero/core/limit"
- "github.com/zeromicro/go-zero/core/stores/redis"
- )
- // TC-0751: M-C 修复回归 —— 对不存在的用户名也执行 dummy bcrypt 比对,
- // 响应文案与"存在用户但密码错"一致,避免用户名枚举。
- func TestValidateProductLogin_UnknownUserSameError(t *testing.T) {
- ctx := context.Background()
- svcCtx := newTestSvcCtx()
- username := "enum_unknown_" + testutil.UniqueId()
- _, err := ValidateProductLogin(ctx, svcCtx, username, "random-pw", "test_product", "127.0.0.1")
- require.Error(t, err)
- var le *LoginError
- require.True(t, errors.As(err, &le))
- assert.Equal(t, 401, le.Code)
- assert.Equal(t, "用户名或密码错误", le.Message,
- "M-C:不存在用户名不得暴露差异化文案")
- }
- // TC-0752: M-C 修复回归 —— 存在用户名但密码错,返回相同文案相同 code,供与 TC-0751 做对照。
- func TestValidateProductLogin_KnownUserWrongPwd(t *testing.T) {
- ctx := context.Background()
- svcCtx := newTestSvcCtx()
- username := "enum_known_" + testutil.UniqueId()
- userId, cleanUser := insertRefreshTestUser(t, ctx, username, "RightPass123", 1, 2)
- t.Cleanup(cleanUser)
- _ = userId
- _, err := ValidateProductLogin(ctx, svcCtx, username, "wrong-pw", "test_product", "127.0.0.1")
- require.Error(t, err)
- var le *LoginError
- require.True(t, errors.As(err, &le))
- assert.Equal(t, 401, le.Code, "M-C:Code 必须与未知用户完全一致")
- assert.Equal(t, "用户名或密码错误", le.Message, "M-C:文案必须与未知用户完全一致")
- }
- // TC-0753: M-C 修复回归 —— UsernameLoginLimit 的 key 必须按 ip:username 构造。
- // 同一 username 不同 IP 的配额互不共用,防止攻击者"用任意 IP 打爆某账号"导致账号 DoS。
- func TestValidateProductLogin_RateLimitKeyedByIPAndUsername(t *testing.T) {
- ctx := context.Background()
- svcCtx := newTestSvcCtx()
- // 使用独立的 quota=1 limiter
- cfg := testutil.GetTestConfig()
- rds := redis.MustNewRedis(cfg.CacheRedis.Nodes[0].RedisConf)
- svcCtx.UsernameLoginLimit = limit.NewPeriodLimit(300, 1, rds,
- cfg.CacheRedis.KeyPrefix+":rl:userlogin:ut:"+testutil.UniqueId())
- username := "enum_rl_" + testutil.UniqueId()
- // IP-A 第 1 次:"用户名或密码错误"
- _, err := ValidateProductLogin(ctx, svcCtx, username, "x", "test_product", "1.1.1.1")
- require.Error(t, err)
- var le *LoginError
- require.True(t, errors.As(err, &le))
- assert.Equal(t, 401, le.Code)
- // IP-A 第 2 次:超限 429
- _, err = ValidateProductLogin(ctx, svcCtx, username, "x", "test_product", "1.1.1.1")
- require.Error(t, err)
- require.True(t, errors.As(err, &le))
- assert.Equal(t, 429, le.Code, "M-C:同 IP 同 username 第 2 次必须触发 429")
- // IP-B 第 1 次:独立桶,仍应走到密码校验(不是 429)
- _, err = ValidateProductLogin(ctx, svcCtx, username, "x", "test_product", "2.2.2.2")
- require.Error(t, err)
- require.True(t, errors.As(err, &le))
- assert.Equal(t, 401, le.Code,
- "M-C:不同 IP 的同 username 必须走独立限流桶(不是 429)")
- }
|