From e11e11cc76c59420e7510f19293565460f233460 Mon Sep 17 00:00:00 2001 From: yokowu <18836617@qq.com> Date: Tue, 4 Aug 2026 16:41:04 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E8=A1=A5=E5=85=85=E7=A9=BA=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E7=A0=81=E7=99=BB=E5=BD=95=E5=9B=9E=E5=BD=92=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team/handler/http/v1/user_captcha_test.go | 33 +++++++++++++++++++ .../biz/user/handler/v1/auth_captcha_test.go | 33 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/backend/biz/team/handler/http/v1/user_captcha_test.go b/backend/biz/team/handler/http/v1/user_captcha_test.go index cfdc9fb85..578eb8354 100644 --- a/backend/biz/team/handler/http/v1/user_captcha_test.go +++ b/backend/biz/team/handler/http/v1/user_captcha_test.go @@ -1,10 +1,12 @@ package v1 import ( + "bytes" "context" "errors" "io" "log/slog" + "net/http" "net/http/httptest" "testing" @@ -47,6 +49,37 @@ func TestTeamLoginCaptchaToggle(t *testing.T) { } } +func TestTeamLoginAcceptsEmptyCaptchaTokenWhenDisabled(t *testing.T) { + tests := []struct { + name string + body string + }{ + {name: "missing", body: `{"email":"admin@example.com","password":"password"}`}, + {name: "empty", body: `{"email":"admin@example.com","password":"password","captcha_token":""}`}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + usecase := &teamLoginUsecaseStub{} + h := &TeamGroupUserHandler{ + logger: slog.New(slog.NewTextHandler(io.Discard, nil)), + usecase: usecase, + captcha: captcha.NewCaptcha(false), + } + w := web.New() + w.POST("/login", web.BindHandler(h.Login)) + + req := httptest.NewRequest(http.MethodPost, "/login", bytes.NewBufferString(tt.body)) + req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) + w.Echo().ServeHTTP(httptest.NewRecorder(), req) + + if !usecase.called { + t.Fatal("Login usecase was not called") + } + }) + } +} + func teamTestWebContext() *web.Context { e := echo.New() req := httptest.NewRequest("POST", "/", nil) diff --git a/backend/biz/user/handler/v1/auth_captcha_test.go b/backend/biz/user/handler/v1/auth_captcha_test.go index 4ca301cff..9a105c0bf 100644 --- a/backend/biz/user/handler/v1/auth_captcha_test.go +++ b/backend/biz/user/handler/v1/auth_captcha_test.go @@ -1,10 +1,12 @@ package v1 import ( + "bytes" "context" "errors" "io" "log/slog" + "net/http" "net/http/httptest" "testing" @@ -47,6 +49,37 @@ func TestPasswordLoginCaptchaToggle(t *testing.T) { } } +func TestPasswordLoginAcceptsEmptyCaptchaTokenWhenDisabled(t *testing.T) { + tests := []struct { + name string + body string + }{ + {name: "missing", body: `{"email":"user@example.com","password":"password"}`}, + {name: "empty", body: `{"email":"user@example.com","password":"password","captcha_token":""}`}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + usecase := &passwordLoginUsecaseStub{} + h := &AuthHandler{ + logger: slog.New(slog.NewTextHandler(io.Discard, nil)), + usecase: usecase, + captcha: captcha.NewCaptcha(false), + } + w := web.New() + w.POST("/login", web.BindHandler(h.PasswordLogin)) + + req := httptest.NewRequest(http.MethodPost, "/login", bytes.NewBufferString(tt.body)) + req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) + w.Echo().ServeHTTP(httptest.NewRecorder(), req) + + if !usecase.called { + t.Fatal("PasswordLogin usecase was not called") + } + }) + } +} + func TestResetPasswordCaptchaToggle(t *testing.T) { tests := []struct { name string From 623061b028bd0663650d42ddb3d8df535912edc3 Mon Sep 17 00:00:00 2001 From: yokowu <18836617@qq.com> Date: Tue, 4 Aug 2026 16:50:43 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=85=B3=E9=97=AD?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81=E5=90=8E=E7=9A=84=E7=A9=BA=E4=BB=A4?= =?UTF-8?q?=E7=89=8C=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/biz/team/handler/http/v1/user.go | 2 +- backend/biz/team/handler/http/v1/user_captcha_test.go | 7 +++++-- backend/biz/user/handler/v1/auth.go | 2 +- backend/biz/user/handler/v1/auth_captcha_test.go | 7 +++++-- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/backend/biz/team/handler/http/v1/user.go b/backend/biz/team/handler/http/v1/user.go index b060c8363..7d79d1dd2 100644 --- a/backend/biz/team/handler/http/v1/user.go +++ b/backend/biz/team/handler/http/v1/user.go @@ -97,7 +97,7 @@ func NewTeamGroupUserHandler(i *do.Injector) (*TeamGroupUserHandler, error) { // @Router /api/v1/teams/users/login [post] func (h *TeamGroupUserHandler) Login(c *web.Context, req domain.TeamLoginReq) error { ctx := c.Request().Context() - if !h.captcha.ValidateToken(ctx, req.CaptchaToken) { + if h.config.Security.CaptchaEnabled && !h.captcha.ValidateToken(ctx, req.CaptchaToken) { return errcode.ErrForbidden } diff --git a/backend/biz/team/handler/http/v1/user_captcha_test.go b/backend/biz/team/handler/http/v1/user_captcha_test.go index 578eb8354..577b6965f 100644 --- a/backend/biz/team/handler/http/v1/user_captcha_test.go +++ b/backend/biz/team/handler/http/v1/user_captcha_test.go @@ -13,6 +13,7 @@ import ( "github.com/GoYoko/web" "github.com/labstack/echo/v4" + "github.com/chaitin/MonkeyCode/backend/config" "github.com/chaitin/MonkeyCode/backend/domain" "github.com/chaitin/MonkeyCode/backend/errcode" "github.com/chaitin/MonkeyCode/backend/pkg/captcha" @@ -33,9 +34,10 @@ func TestTeamLoginCaptchaToggle(t *testing.T) { t.Run(tt.name, func(t *testing.T) { usecase := &teamLoginUsecaseStub{} h := &TeamGroupUserHandler{ + config: &config.Config{Security: config.Security{CaptchaEnabled: tt.enabled}}, logger: slog.New(slog.NewTextHandler(io.Discard, nil)), usecase: usecase, - captcha: captcha.NewCaptcha(tt.enabled), + captcha: captcha.NewCaptcha(), } err := h.Login(teamTestWebContext(), domain.TeamLoginReq{}) @@ -62,9 +64,10 @@ func TestTeamLoginAcceptsEmptyCaptchaTokenWhenDisabled(t *testing.T) { t.Run(tt.name, func(t *testing.T) { usecase := &teamLoginUsecaseStub{} h := &TeamGroupUserHandler{ + config: &config.Config{Security: config.Security{CaptchaEnabled: false}}, logger: slog.New(slog.NewTextHandler(io.Discard, nil)), usecase: usecase, - captcha: captcha.NewCaptcha(false), + captcha: captcha.NewCaptcha(), } w := web.New() w.POST("/login", web.BindHandler(h.Login)) diff --git a/backend/biz/user/handler/v1/auth.go b/backend/biz/user/handler/v1/auth.go index abd6590be..fded268e5 100644 --- a/backend/biz/user/handler/v1/auth.go +++ b/backend/biz/user/handler/v1/auth.go @@ -242,7 +242,7 @@ func oauthErrorCode(err error) string { // @Router /api/v1/users/password-login [post] func (h *AuthHandler) PasswordLogin(c *web.Context, req domain.TeamLoginReq) error { ctx := c.Request().Context() - if !h.captcha.ValidateToken(ctx, req.CaptchaToken) { + if h.config.Security.CaptchaEnabled && !h.captcha.ValidateToken(ctx, req.CaptchaToken) { return errcode.ErrForbidden } diff --git a/backend/biz/user/handler/v1/auth_captcha_test.go b/backend/biz/user/handler/v1/auth_captcha_test.go index 9a105c0bf..4214c694c 100644 --- a/backend/biz/user/handler/v1/auth_captcha_test.go +++ b/backend/biz/user/handler/v1/auth_captcha_test.go @@ -13,6 +13,7 @@ import ( "github.com/GoYoko/web" "github.com/labstack/echo/v4" + "github.com/chaitin/MonkeyCode/backend/config" "github.com/chaitin/MonkeyCode/backend/domain" "github.com/chaitin/MonkeyCode/backend/errcode" "github.com/chaitin/MonkeyCode/backend/pkg/captcha" @@ -33,9 +34,10 @@ func TestPasswordLoginCaptchaToggle(t *testing.T) { t.Run(tt.name, func(t *testing.T) { usecase := &passwordLoginUsecaseStub{} h := &AuthHandler{ + config: &config.Config{Security: config.Security{CaptchaEnabled: tt.enabled}}, logger: slog.New(slog.NewTextHandler(io.Discard, nil)), usecase: usecase, - captcha: captcha.NewCaptcha(tt.enabled), + captcha: captcha.NewCaptcha(), } err := h.PasswordLogin(testWebContext(), domain.TeamLoginReq{}) @@ -62,9 +64,10 @@ func TestPasswordLoginAcceptsEmptyCaptchaTokenWhenDisabled(t *testing.T) { t.Run(tt.name, func(t *testing.T) { usecase := &passwordLoginUsecaseStub{} h := &AuthHandler{ + config: &config.Config{Security: config.Security{CaptchaEnabled: false}}, logger: slog.New(slog.NewTextHandler(io.Discard, nil)), usecase: usecase, - captcha: captcha.NewCaptcha(false), + captcha: captcha.NewCaptcha(), } w := web.New() w.POST("/login", web.BindHandler(h.PasswordLogin))