Smail/backend/controllers/password_capacity_test.go
smail b4f5f51c0e
Some checks are pending
Security Checks / security (push) Waiting to run
保存认证容量保护整改检查点待远程复审
2026-09-17 12:00:45 +08:00

87 lines
3.4 KiB
Go

package controllers
import (
"fmt"
"net/http"
"net/http/httptest"
"path/filepath"
"strings"
"testing"
"git.pcux.cn/xuans/Smail/config"
"git.pcux.cn/xuans/Smail/db"
"git.pcux.cn/xuans/Smail/internal/passwordtest"
"git.pcux.cn/xuans/Smail/models"
"git.pcux.cn/xuans/Smail/services/auth"
"git.pcux.cn/xuans/Smail/session"
appcontext "git.pcux.cn/xuans/Smail/utils/context"
"git.pcux.cn/xuans/Smail/utils/password"
"github.com/alexedwards/scs/v2"
)
func TestPasswordBusyHTTPDoesNotLockAccounts(t *testing.T) {
oldConfig, oldDB, oldSession := config.Instance, db.Instance, session.Instance
oldPair, oldAccount, oldIP := loginPairAttempts, loginAccountAttempts, loginIPAttempts
oldReauthUser, oldReauthIP := reauthUserAttempts, reauthIPAttempts
config.Instance = &config.Config{DbType: config.DBTypeSQLite, DbDSN: filepath.Join(t.TempDir(), "capacity.db")}
if err := db.Init(""); err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
_ = db.Instance.Close()
config.Instance, db.Instance, session.Instance = oldConfig, oldDB, oldSession
loginPairAttempts, loginAccountAttempts, loginIPAttempts = oldPair, oldAccount, oldIP
reauthUserAttempts, reauthIPAttempts = oldReauthUser, oldReauthIP
})
loginPairAttempts, loginAccountAttempts, loginIPAttempts = newLoginLimiter(1), newLoginLimiter(1), newLoginLimiter(1)
reauthUserAttempts, reauthIPAttempts = newLoginLimiter(1), newLoginLimiter(1)
session.Instance = scs.New()
user := models.User{Account: "capacity-user", SessionVersion: 1}
if err := auth.SetPassword(&user, "CorrectP12"); err != nil {
t.Fatal(err)
}
if _, err := db.Instance.Insert(&user); err != nil {
t.Fatal(err)
}
release := passwordtest.Hold(t)
cases := []struct {
handler func(*appcontext.Context, http.ResponseWriter, *http.Request)
body string
}{
{Login, `{"account":"capacity-user","password":"CorrectP12"}`},
{Login, `{"account":"missing-user","password":"CorrectP12"}`},
{ReauthenticateCurrentUser, `{"password":"CorrectP12"}`},
{ModifyPassword, `{"current_password":"CorrectP12","password":"NewPass123"}`},
}
for index := 0; index < 12; index++ {
for _, item := range cases {
request := httptest.NewRequest(http.MethodPost, "https://mail.example.com/api/test", strings.NewReader(item.body))
request.RemoteAddr = "192.0.2.71:1234"
recorder := httptest.NewRecorder()
ctx := &appcontext.Context{Context: request.Context(), UserID: user.ID, UserAccount: user.Account}
item.handler(ctx, recorder, request)
if recorder.Code != http.StatusServiceUnavailable || recorder.Header().Get("Retry-After") != "1" || !strings.Contains(recorder.Body.String(), "AUTH_BUSY") {
t.Fatalf("认证繁忙响应不正确:%d %s", recorder.Code, recorder.Body.String())
}
}
}
for _, limiter := range []*loginLimiter{loginPairAttempts, loginAccountAttempts, loginIPAttempts, reauthUserAttempts, reauthIPAttempts} {
if len(limiter.attempts) != 0 {
t.Fatal("认证繁忙增加了登录失败计数")
}
}
release()
if _, err := auth.Authenticate(user.Account, "CorrectP12"); err != nil {
t.Fatal(err)
}
}
func TestPasswordBusyResponsePreservesOtherErrors(t *testing.T) {
if writePasswordBusy(httptest.NewRecorder(), fmt.Errorf("wrapped: %w", password.ErrInvalidLength)) {
t.Fatal("普通错误被错误标记为资源繁忙")
}
if !writePasswordBusy(httptest.NewRecorder(), fmt.Errorf("wrapped: %w", password.ErrBusy)) {
t.Fatal("包装后的繁忙错误未被识别")
}
}