Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions custom/conf/app.example.ini
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,13 @@ INTERNAL_TOKEN =
;REVERSE_PROXY_AUTHENTICATION_EMAIL = X-WEBAUTH-EMAIL
;REVERSE_PROXY_AUTHENTICATION_FULL_NAME = X-WEBAUTH-FULLNAME
;;
;; URL or path that Gitea should redirect users to *before* performing its
;; own logout. Use this when logout is handled by a reverse proxy or SSO.
;; The external logout endpoint (reverse proxy / IdP) must then redirect
;; the user back to /user/logout so Gitea can terminate its local session
;; after the global SSO logout completes.
;REVERSE_PROXY_LOGOUT_REDIRECT = /mellon/logout?ReturnTo=/user/logout
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it makes sense to hardcode one specific provider here in the default value.
If anything, write the path in the comment above as an example for Mellon (I guess?) and leave this value empty.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, thank you!

;;
;; Interpret X-Forwarded-For header or the X-Real-IP header and set this as the remote IP for the request
;REVERSE_PROXY_LIMIT = 1
;;
Expand Down
2 changes: 2 additions & 0 deletions modules/setting/security.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ var (
ReverseProxyAuthEmail string
ReverseProxyAuthFullName string
ReverseProxyLimit int
ReverseProxyLogoutRedirect string
ReverseProxyTrustedProxies []string
MinPasswordLength int
ImportLocalPaths bool
Expand Down Expand Up @@ -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"}
Expand Down
3 changes: 3 additions & 0 deletions modules/templates/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ func NewFuncMap() template.FuncMap {
"MermaidMaxSourceCharacters": func() int {
return setting.MermaidMaxSourceCharacters
},
"ReverseProxyLogoutRedirect": func() string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a global variable rather than a global function

return setting.ReverseProxyLogoutRedirect
},

// -----------------------------------------------------------------
// render
Expand Down
4 changes: 4 additions & 0 deletions routers/web/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,10 @@ func SignOut(ctx *context.Context) {
})
}
HandleSignOut(ctx)
if ctx.Req.Method == http.MethodGet {
ctx.Redirect(setting.AppSubURL + "/")
return
}
ctx.JSONRedirect(setting.AppSubURL + "/")
}

Expand Down
1 change: 1 addition & 0 deletions routers/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 6 additions & 2 deletions templates/base/head_navbar.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@
</div>

<div class="divider"></div>
<a class="item link-action" href data-url="{{AppSubUrl}}/user/logout">
<a class="item {{if not ReverseProxyLogoutRedirect}}link-action{{end}}"
{{if ReverseProxyLogoutRedirect}}href="{{ReverseProxyLogoutRedirect}}"
{{else}}href data-url="{{AppSubUrl}}/user/logout"{{end}}>
{{svg "octicon-sign-out"}}
{{ctx.Locale.Tr "sign_out"}}
</a>
Expand Down Expand Up @@ -128,7 +130,9 @@
</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{{end}}"
{{if ReverseProxyLogoutRedirect}}href="{{ReverseProxyLogoutRedirect}}"
{{else}}href data-url="{{AppSubUrl}}/user/logout"{{end}}>
{{svg "octicon-sign-out"}}
{{ctx.Locale.Tr "sign_out"}}
</a>
Expand Down
41 changes: 40 additions & 1 deletion tests/integration/signout_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ package integration

import (
"net/http"
"strings"
"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")
Expand All @@ -22,3 +25,39 @@ 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=/user/logout")()

session := loginUser(t, "user2")

req := NewRequest(t, "GET", "/")
resp := session.MakeRequest(t, req, http.StatusOK)

body := resp.Body.String()

// check that the external URL is present in the logout button
if !strings.Contains(body, `href="/mellon/logout?ReturnTo=/user/logout"`) {
t.Fatalf("logout button does not point to REVERSE_PROXY_LOGOUT_REDIRECT when configured")
}
}