From ad789d8c92466323ca93f1820ed71a67834b3201 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 15 Nov 2024 22:05:55 -0800 Subject: [PATCH 1/2] Fix basic auth with webauthn --- services/auth/basic.go | 10 ++++++++ tests/integration/api_twofa_test.go | 36 +++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/services/auth/basic.go b/services/auth/basic.go index 90bd64237091d..0f22c29db5308 100644 --- a/services/auth/basic.go +++ b/services/auth/basic.go @@ -5,6 +5,7 @@ package auth import ( + "errors" "net/http" "strings" @@ -140,6 +141,15 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore return nil, err } + // Check if the user has webAuthn registration + hasWebAuthn, err := auth_model.HasWebAuthnRegistrationsByUID(req.Context(), u.ID) + if err != nil { + return nil, err + } + if hasWebAuthn { + return nil, errors.New("Basic authorization is not allowed while webAuthn enrolled") + } + if skipper, ok := source.Cfg.(LocalTwoFASkipper); !ok || !skipper.IsSkipLocalTwoFA() { if err := validateTOTP(req, u); err != nil { return nil, err diff --git a/tests/integration/api_twofa_test.go b/tests/integration/api_twofa_test.go index aad806b6dc4ff..07532bc80cf16 100644 --- a/tests/integration/api_twofa_test.go +++ b/tests/integration/api_twofa_test.go @@ -53,3 +53,39 @@ func TestAPITwoFactor(t *testing.T) { req.Header.Set("X-Gitea-OTP", passcode) MakeRequest(t, req, http.StatusOK) } + +func TestBasicAuthWithWebAuthn(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + // user1 has no webauthn enrolled, he can request API with basic auth + user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + unittest.AssertNotExistsBean(t, &auth_model.WebAuthnCredential{UserID: user1.ID}) + req := NewRequest(t, "GET", "/api/v1/user") + req.SetBasicAuth(user1.Name, "password") + MakeRequest(t, req, http.StatusOK) + + // user1 has webauthn enrolled, he can request git protocol with basic auth + req = NewRequest(t, "GET", "/user2/repo1/info/refs") + req.SetBasicAuth(user1.Name, "password") + MakeRequest(t, req, http.StatusOK) + + // user32 has webauthn enrolled, he can't request API with basic auth + user32 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 32}) + unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{UserID: user32.ID}) + + req = NewRequest(t, "GET", "/api/v1/user") + req.SetBasicAuth(user32.Name, "notpassword") + resp := MakeRequest(t, req, http.StatusUnauthorized) + + type userResponse struct { + Message string `json:"message"` + } + var userParsed userResponse + DecodeJSON(t, resp, &userParsed) + assert.EqualValues(t, "Basic authorization is not allowed while webAuthn enrolled", userParsed.Message) + + // user32 has webauthn enrolled, he can't request git protocol with basic auth + req = NewRequest(t, "GET", "/user2/repo1/info/refs") + req.SetBasicAuth(user32.Name, "notpassword") + MakeRequest(t, req, http.StatusUnauthorized) +} From 7b58e2604b433fa0f287e5eecf21a87b2e296504 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 15 Nov 2024 22:21:06 -0800 Subject: [PATCH 2/2] Add container test --- services/auth/basic.go | 18 +++++++++--------- tests/integration/api_twofa_test.go | 21 +++++++++++++++++++-- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/services/auth/basic.go b/services/auth/basic.go index 0f22c29db5308..1f6c3a442d1d8 100644 --- a/services/auth/basic.go +++ b/services/auth/basic.go @@ -141,16 +141,16 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore return nil, err } - // Check if the user has webAuthn registration - hasWebAuthn, err := auth_model.HasWebAuthnRegistrationsByUID(req.Context(), u.ID) - if err != nil { - return nil, err - } - if hasWebAuthn { - return nil, errors.New("Basic authorization is not allowed while webAuthn enrolled") - } - if skipper, ok := source.Cfg.(LocalTwoFASkipper); !ok || !skipper.IsSkipLocalTwoFA() { + // Check if the user has webAuthn registration + hasWebAuthn, err := auth_model.HasWebAuthnRegistrationsByUID(req.Context(), u.ID) + if err != nil { + return nil, err + } + if hasWebAuthn { + return nil, errors.New("Basic authorization is not allowed while webAuthn enrolled") + } + if err := validateTOTP(req, u); err != nil { return nil, err } diff --git a/tests/integration/api_twofa_test.go b/tests/integration/api_twofa_test.go index 07532bc80cf16..18e6fa91b7e6c 100644 --- a/tests/integration/api_twofa_test.go +++ b/tests/integration/api_twofa_test.go @@ -64,18 +64,30 @@ func TestBasicAuthWithWebAuthn(t *testing.T) { req.SetBasicAuth(user1.Name, "password") MakeRequest(t, req, http.StatusOK) - // user1 has webauthn enrolled, he can request git protocol with basic auth + // user1 has no webauthn enrolled, he can request git protocol with basic auth req = NewRequest(t, "GET", "/user2/repo1/info/refs") req.SetBasicAuth(user1.Name, "password") MakeRequest(t, req, http.StatusOK) + // user1 has no webauthn enrolled, he can request container package with basic auth + req = NewRequest(t, "GET", "/v2/token") + req.SetBasicAuth(user1.Name, "password") + resp := MakeRequest(t, req, http.StatusOK) + + type tokenResponse struct { + Token string `json:"token"` + } + var tokenParsed tokenResponse + DecodeJSON(t, resp, &tokenParsed) + assert.NotEmpty(t, tokenParsed.Token) + // user32 has webauthn enrolled, he can't request API with basic auth user32 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 32}) unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{UserID: user32.ID}) req = NewRequest(t, "GET", "/api/v1/user") req.SetBasicAuth(user32.Name, "notpassword") - resp := MakeRequest(t, req, http.StatusUnauthorized) + resp = MakeRequest(t, req, http.StatusUnauthorized) type userResponse struct { Message string `json:"message"` @@ -88,4 +100,9 @@ func TestBasicAuthWithWebAuthn(t *testing.T) { req = NewRequest(t, "GET", "/user2/repo1/info/refs") req.SetBasicAuth(user32.Name, "notpassword") MakeRequest(t, req, http.StatusUnauthorized) + + // user32 has webauthn enrolled, he can't request container package with basic auth + req = NewRequest(t, "GET", "/v2/token") + req.SetBasicAuth(user1.Name, "notpassword") + MakeRequest(t, req, http.StatusUnauthorized) }