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)") }