fix(auth): 协议登录(IMAP/SMTP/POP3)成功时清零失败计数 + 支持裸用户名

问题:ResetFail 只在 Web 登录调用,协议层失败计数只增不减。
合法用户的客户端(手机 APP 用裸用户名重试、配置向导探测、输错
密码等)失败次数持续累积,每达到阈值就触发一次封禁档位,从第 4
次触发起真实封禁 30 分钟+——用户被反复误封,手机端表现为一直
卡在"正在接收邮件"。

修复:
- IMAP/SMTP/POP3 认证成功路径调用 Bans.ResetFail(与 Web 一致)
- 新增 UserStore.AuthenticateLogin:支持裸用户名(如 "kevin"),
  唯一归属时自动解析到其域名;跨域名同名歧义时要求完整邮箱
- 新增 TestAuthenticateLoginBareUsername 单元测试
This commit is contained in:
dsh
2026-08-19 10:12:28 -04:00
parent 6d5a7a8ed4
commit f48776e89b
5 changed files with 122 additions and 3 deletions
+6 -1
View File
@@ -145,7 +145,7 @@ func (b *imapBackend) Login(connInfo *imap.ConnInfo, username, password string)
return nil, backend.ErrInvalidCredentials
}
user, err := b.stores.Users.Authenticate(username, password)
user, err := b.stores.Users.AuthenticateLogin(username, password)
if err != nil {
// 认证失败计数,达到阈值按档位封禁(与 Web 登录共用 ban_entries
b.stores.RecordAuthFailure(clientIP, b.banCfg.MaxFailAttempts, b.banCfg.BanDurationMin, "邮件协议认证失败次数过多")
@@ -153,6 +153,11 @@ func (b *imapBackend) Login(connInfo *imap.ConnInfo, username, password string)
return nil, fmt.Errorf("invalid credentials: %w", err)
}
// 登录成功清零失败计数(与 Web 登录一致):否则协议客户端的失败计数
// 只增不减(如配置探测、输错密码、APP 用裸用户名重试等),累计触发
// 档位封禁,合法用户 IP 被反复误封。
b.stores.Bans.ResetFail(clientIP)
email := user.Username + "@"
domain, err := b.stores.Domains.GetByID(user.DomainID)
if err == nil {