0
0
mirror of https://github.com/go-gitea/gitea.git synced 2025-12-09 04:41:48 +01:00

Merge 23fd5f9e72e25326c193afc19ea7212cf59ff358 into 69700f9cddddc8a62641e42f0c2dd6d5db794223

This commit is contained in:
Elisei Roca 2025-12-08 15:29:58 +08:00 committed by GitHub
commit 5244ff4e89
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 62 additions and 3 deletions

View File

@ -463,6 +463,11 @@ INTERNAL_TOKEN =
;; Name of cookie used to store authentication information.
;COOKIE_REMEMBER_NAME = gitea_incredible
;;
;; URL or path that Gitea should redirect users to *after* performing its own logout.
;; Use this to redirect user to the external logout endpoint, if needed, when authentication is handled by a reverse proxy or SSO.
;; Mellon example: REVERSE_PROXY_LOGOUT_REDIRECT = /mellon/logout?ReturnTo=/
;REVERSE_PROXY_LOGOUT_REDIRECT =
;;
;; Reverse proxy authentication header name of user name, email, and full name
;REVERSE_PROXY_AUTHENTICATION_USER = X-WEBAUTH-USER
;REVERSE_PROXY_AUTHENTICATION_EMAIL = X-WEBAUTH-EMAIL

View File

@ -25,6 +25,7 @@ var (
ReverseProxyAuthEmail string
ReverseProxyAuthFullName string
ReverseProxyLimit int
ReverseProxyLogoutRedirect string
ReverseProxyTrustedProxies []string
MinPasswordLength int
ImportLocalPaths bool
@ -121,6 +122,7 @@ func loadSecurityFrom(rootCfg ConfigProvider) {
ReverseProxyAuthFullName = sec.Key("REVERSE_PROXY_AUTHENTICATION_FULL_NAME").MustString("X-WEBAUTH-FULLNAME")
ReverseProxyLimit = sec.Key("REVERSE_PROXY_LIMIT").MustInt(1)
ReverseProxyLogoutRedirect = sec.Key("REVERSE_PROXY_LOGOUT_REDIRECT").MustString("")
ReverseProxyTrustedProxies = sec.Key("REVERSE_PROXY_TRUSTED_PROXIES").Strings(",")
if len(ReverseProxyTrustedProxies) == 0 {
ReverseProxyTrustedProxies = []string{"127.0.0.0/8", "::1/128"}

View File

@ -416,6 +416,14 @@ func SignOut(ctx *context.Context) {
})
}
HandleSignOut(ctx)
if setting.ReverseProxyLogoutRedirect != "" {
ctx.Redirect(setting.ReverseProxyLogoutRedirect)
return
}
if ctx.Req.Method == http.MethodGet {
ctx.Redirect(setting.AppSubURL + "/")
return
}
ctx.JSONRedirect(setting.AppSubURL + "/")
}

View File

@ -694,6 +694,7 @@ func registerWebRoutes(m *web.Router) {
m.Post("/recover_account", auth.ResetPasswdPost)
m.Get("/forgot_password", auth.ForgotPasswd)
m.Post("/forgot_password", auth.ForgotPasswdPost)
m.Get("/logout", auth.SignOut)
m.Post("/logout", auth.SignOut)
m.Get("/stopwatches", reqSignIn, user.GetStopwatches)
m.Get("/search_candidates", optExploreSignIn, user.SearchCandidates)

View File

@ -206,6 +206,8 @@ func Contexter() func(next http.Handler) http.Handler {
ctx.Data["ManifestData"] = setting.ManifestData
ctx.Data["AllLangs"] = translation.AllLangs()
ctx.Data["ReverseProxyLogoutRedirect"] = setting.ReverseProxyLogoutRedirect != ""
next.ServeHTTP(ctx.Resp, ctx.Req)
})
}

View File

@ -55,7 +55,7 @@
</div>
<div class="divider"></div>
<a class="item link-action" href data-url="{{AppSubUrl}}/user/logout">
<a class="item{{if not .ReverseProxyLogoutRedirect}} link-action" data-url={{else}}" href={{end}}"{{AppSubUrl}}/user/logout">
{{svg "octicon-sign-out"}}
{{ctx.Locale.Tr "sign_out"}}
</a>
@ -128,7 +128,7 @@
</a>
{{end}}
<div class="divider"></div>
<a class="item link-action" href data-url="{{AppSubUrl}}/user/logout">
<a class="item{{if not .ReverseProxyLogoutRedirect}} link-action" data-url={{else}}" href={{end}}"{{AppSubUrl}}/user/logout">
{{svg "octicon-sign-out"}}
{{ctx.Locale.Tr "sign_out"}}
</a>

View File

@ -7,10 +7,12 @@ import (
"net/http"
"testing"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/tests"
)
func TestSignOut(t *testing.T) {
func TestSignOut_Post(t *testing.T) {
defer tests.PrepareTestEnv(t)()
session := loginUser(t, "user2")
@ -22,3 +24,42 @@ func TestSignOut(t *testing.T) {
req = NewRequest(t, "GET", "/user2/repo2")
session.MakeRequest(t, req, http.StatusNotFound)
}
func TestSignOut_Get(t *testing.T) {
defer tests.PrepareTestEnv(t)()
session := loginUser(t, "user2")
req := NewRequest(t, "GET", "/user/logout")
resp := session.MakeRequest(t, req, http.StatusSeeOther)
location := resp.Header().Get("Location")
if location != "/" {
t.Fatalf("expected redirect Location to '/', got %q", location)
}
// try to view a private repo, should fail
req = NewRequest(t, "GET", "/user2/repo2")
session.MakeRequest(t, req, http.StatusNotFound)
}
func TestSignOut_ReverseProxyLogoutRedirect(t *testing.T) {
defer tests.PrepareTestEnv(t)()
defer test.MockVariableValue(&setting.ReverseProxyLogoutRedirect, "/mellon/logout?ReturnTo=/")()
session := loginUser(t, "user2")
req := NewRequest(t, "GET", "/user/logout")
resp := session.MakeRequest(t, req, http.StatusSeeOther)
expected := "/mellon/logout?ReturnTo=/"
loc := resp.Header().Get("Location")
if loc != expected {
t.Fatalf("expected redirect to %q, got %q", expected, loc)
}
// try to view a private repo, should fail
req = NewRequest(t, "GET", "/user2/repo2")
session.MakeRequest(t, req, http.StatusNotFound)
}