From cb0e41c738dc28ed16161f9d032fc35e8945c570 Mon Sep 17 00:00:00 2001 From: smail Date: Fri, 2 Oct 2026 21:36:11 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=87=AD=E6=8D=AE=E6=9F=A5?= =?UTF-8?q?=E7=9C=8B=E5=9B=9E=E5=BD=92=E7=94=A8=E4=BE=8B=E9=81=BF=E5=85=8D?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E6=8B=92=E7=BB=9D=E9=80=A0=E6=88=90=E5=81=87?= =?UTF-8?q?=E8=A6=86=E7=9B=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security_authentication_test.go | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/backend/controllers/security_authentication_test.go b/backend/controllers/security_authentication_test.go index 0f5110d7..02d2ecbe 100644 --- a/backend/controllers/security_authentication_test.go +++ b/backend/controllers/security_authentication_test.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" "net/http/httptest" + "strconv" "strings" "testing" @@ -329,25 +330,39 @@ func TestClientCredentialEntryPointsRequireOwnPassword(t *testing.T) { {"create", CreateClientCredential, `{"client_name":"新设备"`}, {"regenerate", RegenerateClientCredential, fmt.Sprintf(`{"client_id":%d`, client.ID)}, {"revoke", RevokeClientCredential, fmt.Sprintf(`{"client_id":%d`, client.ID)}, - {"reveal", RevealClientCredentials, `{"unused":true`}, + {"reveal", RevealClientCredentials, `{`}, {"enable protocol", SetProtocolCredentialEnabled, `{"protocol":"imap","enabled":true`}, {"disable protocol", SetProtocolCredentialEnabled, `{"protocol":"smtp","enabled":false`}, {"generate protocol", GenerateProtocolCredential, `{"protocol":"smtp"`}, {"revoke protocol", RevokeProtocolCredential, `{"protocol":"smtp"`}, - {"reveal protocol", RevealProtocolCredentials, `{"unused":true`}, + {"reveal protocol", RevealProtocolCredentials, `{`}, } for _, role := range []access.Role{access.RoleUser, access.RoleAdmin, access.RoleSuperAdmin} { for _, entry := range entries { t.Run(string(role)+"/"+entry.name, func(t *testing.T) { reauthUserAttempts, reauthIPAttempts = newLoginLimiter(10), newLoginLimiter(50) ctx := &appcontext.Context{UserID: user.ID, Role: role, RecentAuth: true} + expectedFailures := 0 for _, suffix := range []string{`}`, `,"password":"incorrect"}`, `,"password":" FixtureP12 "}`} { + if entry.body == "{" { + suffix = strings.TrimPrefix(suffix, ",") + } recorder := httptest.NewRecorder() entry.hand(ctx, recorder, httptest.NewRequest(http.MethodPost, "/api/security", strings.NewReader(entry.body+suffix))) var result response.Response if err := json.Unmarshal(recorder.Body.Bytes(), &result); err != nil || result.ErrorNo == 0 { t.Fatal("missing or incorrect password must not allow credential operations") } + if suffix != "}" { + expectedFailures++ + key := reauthUserAttempts.accountKey(strconv.Itoa(user.ID)) + reauthUserAttempts.mu.Lock() + failures := reauthUserAttempts.attempts[key].failures + reauthUserAttempts.mu.Unlock() + if failures != expectedFailures { + t.Fatal("incorrect password must reach authentication and record a failed attempt") + } + } if recorder.Header().Get("Cache-Control") != "no-store" { t.Fatal("credential responses must not be cached") }