mirror of
https://github.com/go-gitea/gitea.git
synced 2025-05-01 13:15:12 +00:00
Fix #880 Design: 1. A global setting `security.TWO_FACTOR_AUTH`. * To support org-level config, we need to introduce a better "owner setting" system first (in the future) 2. A user without 2FA can login and may explore, but can NOT read or write to any repositories via API/web. 3. Keep things as simple as possible. * This option only aggressively suggest users to enable their 2FA at the moment, it does NOT guarantee that users must have 2FA before all other operations, it should be good enough for real world use cases. * Some details and tests could be improved in the future since this change only adds a check and seems won't affect too much. --------- Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
109 lines
3.3 KiB
Go
109 lines
3.3 KiB
Go
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package integration
|
|
|
|
import (
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
auth_model "code.gitea.io/gitea/models/auth"
|
|
"code.gitea.io/gitea/models/db"
|
|
"code.gitea.io/gitea/models/unittest"
|
|
user_model "code.gitea.io/gitea/models/user"
|
|
"code.gitea.io/gitea/tests"
|
|
|
|
"github.com/pquerna/otp/totp"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestAPITwoFactor(t *testing.T) {
|
|
defer tests.PrepareTestEnv(t)()
|
|
|
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 16})
|
|
|
|
req := NewRequest(t, "GET", "/api/v1/user").
|
|
AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusOK)
|
|
|
|
otpKey, err := totp.Generate(totp.GenerateOpts{
|
|
SecretSize: 40,
|
|
Issuer: "gitea-test",
|
|
AccountName: user.Name,
|
|
})
|
|
assert.NoError(t, err)
|
|
|
|
tfa := &auth_model.TwoFactor{
|
|
UID: user.ID,
|
|
}
|
|
assert.NoError(t, tfa.SetSecret(otpKey.Secret()))
|
|
|
|
assert.NoError(t, auth_model.NewTwoFactor(db.DefaultContext, tfa))
|
|
|
|
req = NewRequest(t, "GET", "/api/v1/user").
|
|
AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusUnauthorized)
|
|
|
|
passcode, err := totp.GenerateCode(otpKey.Secret(), time.Now())
|
|
assert.NoError(t, err)
|
|
|
|
req = NewRequest(t, "GET", "/api/v1/user").
|
|
AddBasicAuth(user.Name)
|
|
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 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)
|
|
|
|
type userResponse struct {
|
|
Message string `json:"message"`
|
|
}
|
|
var userParsed userResponse
|
|
DecodeJSON(t, resp, &userParsed)
|
|
assert.Equal(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)
|
|
|
|
// 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)
|
|
}
|