summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwxiaoguang <wxiaoguang@gmail.com>2023-09-18 07:32:56 +0800
committerGitHub <noreply@github.com>2023-09-17 23:32:56 +0000
commit8531ca08372dd4a4739564dec17766fffe34a385 (patch)
treea6c05c2083d71230bab7768d205e7fbfedcebd4c
parent47b878858ada27fc4c74eeadcc1e467d2da90e04 (diff)
downloadgitea-8531ca08372dd4a4739564dec17766fffe34a385.tar.gz
gitea-8531ca08372dd4a4739564dec17766fffe34a385.zip
Make SSPI auth mockable (#27036)
Before, the SSPI auth is only complied for Windows, it's difficult to test and it breaks a lot. Now, make the SSPI auth mockable and testable.
-rw-r--r--routers/api/v1/api.go5
-rw-r--r--routers/api/v1/auth.go10
-rw-r--r--routers/api/v1/auth_windows.go19
-rw-r--r--routers/web/auth.go10
-rw-r--r--routers/web/auth_windows.go19
-rw-r--r--routers/web/web.go6
-rw-r--r--services/auth/sspi.go (renamed from services/auth/sspi_windows.go)30
-rw-r--r--services/auth/sspiauth_posix.go30
-rw-r--r--services/auth/sspiauth_windows.go19
9 files changed, 72 insertions, 76 deletions
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index ca74a23a4b..d58e39920b 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -705,7 +705,10 @@ func buildAuthGroup() *auth.Group {
if setting.Service.EnableReverseProxyAuthAPI {
group.Add(&auth.ReverseProxy{})
}
- specialAdd(group)
+
+ if setting.IsWindows && auth_model.IsSSPIEnabled() {
+ group.Add(&auth.SSPI{}) // it MUST be the last, see the comment of SSPI
+ }
return group
}
diff --git a/routers/api/v1/auth.go b/routers/api/v1/auth.go
deleted file mode 100644
index e44271ba14..0000000000
--- a/routers/api/v1/auth.go
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2022 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-//go:build !windows
-
-package v1
-
-import auth_service "code.gitea.io/gitea/services/auth"
-
-func specialAdd(group *auth_service.Group) {}
diff --git a/routers/api/v1/auth_windows.go b/routers/api/v1/auth_windows.go
deleted file mode 100644
index 3514e21baa..0000000000
--- a/routers/api/v1/auth_windows.go
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2022 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package v1
-
-import (
- "code.gitea.io/gitea/models/auth"
- auth_service "code.gitea.io/gitea/services/auth"
-)
-
-// specialAdd registers the SSPI auth method as the last method in the list.
-// The SSPI plugin is expected to be executed last, as it returns 401 status code if negotiation
-// fails (or if negotiation should continue), which would prevent other authentication methods
-// to execute at all.
-func specialAdd(group *auth_service.Group) {
- if auth.IsSSPIEnabled() {
- group.Add(&auth_service.SSPI{})
- }
-}
diff --git a/routers/web/auth.go b/routers/web/auth.go
deleted file mode 100644
index 1ca860ecc8..0000000000
--- a/routers/web/auth.go
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2022 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-//go:build !windows
-
-package web
-
-import auth_service "code.gitea.io/gitea/services/auth"
-
-func specialAdd(group *auth_service.Group) {}
diff --git a/routers/web/auth_windows.go b/routers/web/auth_windows.go
deleted file mode 100644
index 3125d7ce9a..0000000000
--- a/routers/web/auth_windows.go
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2022 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package web
-
-import (
- "code.gitea.io/gitea/models/auth"
- auth_service "code.gitea.io/gitea/services/auth"
-)
-
-// specialAdd registers the SSPI auth method as the last method in the list.
-// The SSPI plugin is expected to be executed last, as it returns 401 status code if negotiation
-// fails (or if negotiation should continue), which would prevent other authentication methods
-// to execute at all.
-func specialAdd(group *auth_service.Group) {
- if auth.IsSSPIEnabled() {
- group.Add(&auth_service.SSPI{})
- }
-}
diff --git a/routers/web/web.go b/routers/web/web.go
index 077a076864..99862505b4 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -8,6 +8,7 @@ import (
"net/http"
"strings"
+ auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/context"
@@ -92,7 +93,10 @@ func buildAuthGroup() *auth_service.Group {
if setting.Service.EnableReverseProxyAuth {
group.Add(&auth_service.ReverseProxy{})
}
- specialAdd(group)
+
+ if setting.IsWindows && auth_model.IsSSPIEnabled() {
+ group.Add(&auth_service.SSPI{}) // it MUST be the last, see the comment of SSPI
+ }
return group
}
diff --git a/services/auth/sspi_windows.go b/services/auth/sspi.go
index e29bd71529..d4f7e3ec60 100644
--- a/services/auth/sspi_windows.go
+++ b/services/auth/sspi.go
@@ -22,19 +22,21 @@ import (
"code.gitea.io/gitea/services/auth/source/sspi"
gouuid "github.com/google/uuid"
- "github.com/quasoft/websspi"
)
const (
tplSignIn base.TplName = "user/auth/signin"
)
+type SSPIAuth interface {
+ AppendAuthenticateHeader(w http.ResponseWriter, data string)
+ Authenticate(r *http.Request, w http.ResponseWriter) (userInfo *SSPIUserInfo, outToken string, err error)
+}
+
var (
- // sspiAuth is a global instance of the websspi authentication package,
- // which is used to avoid acquiring the server credential handle on
- // every request
- sspiAuth *websspi.Authenticator
- sspiAuthOnce sync.Once
+ sspiAuth SSPIAuth // a global instance of the websspi authenticator to avoid acquiring the server credential handle on every request
+ sspiAuthOnce sync.Once
+ sspiAuthErrInit error
// Ensure the struct implements the interface.
_ Method = &SSPI{}
@@ -42,8 +44,9 @@ var (
// SSPI implements the SingleSignOn interface and authenticates requests
// via the built-in SSPI module in Windows for SPNEGO authentication.
-// On successful authentication returns a valid user object.
-// Returns nil if authentication fails.
+// The SSPI plugin is expected to be executed last, as it returns 401 status code if negotiation
+// fails (or if negotiation should continue), which would prevent other authentication methods
+// to execute at all.
type SSPI struct{}
// Name represents the name of auth method
@@ -56,15 +59,10 @@ func (s *SSPI) Name() string {
// If negotiation should continue or authentication fails, immediately returns a 401 HTTP
// response code, as required by the SPNEGO protocol.
func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
- var errInit error
- sspiAuthOnce.Do(func() {
- config := websspi.NewConfig()
- sspiAuth, errInit = websspi.New(config)
- })
- if errInit != nil {
- return nil, errInit
+ sspiAuthOnce.Do(func() { sspiAuthErrInit = sspiAuthInit() })
+ if sspiAuthErrInit != nil {
+ return nil, sspiAuthErrInit
}
-
if !s.shouldAuthenticate(req) {
return nil, nil
}
diff --git a/services/auth/sspiauth_posix.go b/services/auth/sspiauth_posix.go
new file mode 100644
index 0000000000..49b0ed4a52
--- /dev/null
+++ b/services/auth/sspiauth_posix.go
@@ -0,0 +1,30 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+//go:build !windows
+
+package auth
+
+import (
+ "errors"
+ "net/http"
+)
+
+type SSPIUserInfo struct {
+ Username string // Name of user, usually in the form DOMAIN\User
+ Groups []string // The global groups the user is a member of
+}
+
+type sspiAuthMock struct{}
+
+func (s sspiAuthMock) AppendAuthenticateHeader(w http.ResponseWriter, data string) {
+}
+
+func (s sspiAuthMock) Authenticate(r *http.Request, w http.ResponseWriter) (userInfo *SSPIUserInfo, outToken string, err error) {
+ return nil, "", errors.New("not implemented")
+}
+
+func sspiAuthInit() error {
+ sspiAuth = &sspiAuthMock{} // TODO: we can mock the SSPI auth in tests
+ return nil
+}
diff --git a/services/auth/sspiauth_windows.go b/services/auth/sspiauth_windows.go
new file mode 100644
index 0000000000..093caaed33
--- /dev/null
+++ b/services/auth/sspiauth_windows.go
@@ -0,0 +1,19 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+//go:build windows
+
+package auth
+
+import (
+ "github.com/quasoft/websspi"
+)
+
+type SSPIUserInfo = websspi.UserInfo
+
+func sspiAuthInit() error {
+ var err error
+ config := websspi.NewConfig()
+ sspiAuth, err = websspi.New(config)
+ return err
+}