From 22441d90ddfdfffd93552634da1831110e04668e Mon Sep 17 00:00:00 2001 From: Pawel Boguslawski Date: Tue, 20 Oct 2020 18:50:05 +0200 Subject: [PATCH] Fixed user session initialization on login with reverse proxy header Gitea does not initialize user session after login using reverse proxy header. This fixes it. Fixes: 53a9b2631a2ddfe12fe49c7244c3ec5aec6c50aa Author-Change-Id: IB#1104925 --- modules/auth/sso/reverseproxy.go | 17 ++- modules/auth/sso/reverseproxy.go.orig | 150 ++++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 5 deletions(-) create mode 100644 modules/auth/sso/reverseproxy.go.orig diff --git a/modules/auth/sso/reverseproxy.go b/modules/auth/sso/reverseproxy.go index 263bb0efb8877..b7632be0a0a0e 100644 --- a/modules/auth/sso/reverseproxy.go +++ b/modules/auth/sso/reverseproxy.go @@ -100,12 +100,19 @@ func (r *ReverseProxy) VerifyAuthData(ctx *macaron.Context, sess session.Store) } } - // Initialize new session. - handleSignIn(ctx, sess, user) + // Make sure requests to API paths and PWA resources do not create a new session. + if !isAPIPath(ctx) && !isAttachmentDownload(ctx) { - user.SetLastLogin() - if err = models.UpdateUserCols(user, false, "last_login_unix"); err != nil { - log.Error(fmt.Sprintf("VerifyAuthData: error updating user last login time [user: %d]", user.ID)) + // Initialize new session. + handleSignIn(ctx, sess, user) + + user.SetLastLogin() + if err = models.UpdateUserCols(user, false, "last_login_unix"); err != nil { + log.Error(fmt.Sprintf("VerifyAuthData: error updating user last login time [user: %d]", user.ID)) + } + + // Redirect to self to update language. + ctx.Redirect(setting.AppSubURL + ctx.Req.URL.RequestURI()) } // Redirect to self to apply user language using cookie. diff --git a/modules/auth/sso/reverseproxy.go.orig b/modules/auth/sso/reverseproxy.go.orig new file mode 100644 index 0000000000000..263bb0efb8877 --- /dev/null +++ b/modules/auth/sso/reverseproxy.go.orig @@ -0,0 +1,150 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package sso + +import ( + "fmt" + "strings" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + + "gitea.com/macaron/macaron" + "gitea.com/macaron/session" + gouuid "github.com/google/uuid" +) + +// Ensure the struct implements the interface. +var ( + _ SingleSignOn = &ReverseProxy{} +) + +// ReverseProxy implements the SingleSignOn interface, but actually relies on +// a reverse proxy for authentication of users. +// On successful authentication the proxy is expected to populate the username in the +// "setting.ReverseProxyAuthUser" header. Optionally it can also populate the email of the +// user in the "setting.ReverseProxyAuthEmail" header. +type ReverseProxy struct { +} + +// getUserName extracts the username from the "setting.ReverseProxyAuthUser" header +func (r *ReverseProxy) getUserName(ctx *macaron.Context) string { + webAuthUser := strings.TrimSpace(ctx.Req.Header.Get(setting.ReverseProxyAuthUser)) + if len(webAuthUser) == 0 { + return "" + } + return webAuthUser +} + +// Init does nothing as the ReverseProxy implementation does not need initialization +func (r *ReverseProxy) Init() error { + return nil +} + +// Free does nothing as the ReverseProxy implementation does not have to release resources +func (r *ReverseProxy) Free() error { + return nil +} + +// IsEnabled checks if EnableReverseProxyAuth setting is true +func (r *ReverseProxy) IsEnabled() bool { + return setting.Service.EnableReverseProxyAuth +} + +// VerifyAuthData extracts the username from the "setting.ReverseProxyAuthUser" header +// of the request and returns the corresponding user object for that name. +// Verification of header data is not performed as it should have already been done by +// the revese proxy. +// If a username is available in the "setting.ReverseProxyAuthUser" header an existing +// user object is returned (populated with username or email found in header). +// Returns nil if header is empty. +func (r *ReverseProxy) VerifyAuthData(ctx *macaron.Context, sess session.Store) *models.User { + + // Just return user if session is estabilshed already. + user := SessionUser(sess) + if user != nil { + return user + } + + // If no session established, get username from header. + username := r.getUserName(ctx) + if len(username) == 0 { + return nil + } + + var err error + + if r.isAutoRegisterAllowed() { + // Use auto registration from reverse proxy if ENABLE_REVERSE_PROXY_AUTO_REGISTRATION enabled. + if user, err = models.GetUserByName(username); err != nil { + if models.IsErrUserNotExist(err) && r.isAutoRegisterAllowed() { + if user = r.newUser(ctx); user == nil { + return nil + } + } else { + log.Error("GetUserByName: %v", err) + return nil + } + } + } else { + // Use auto registration from other backends if ENABLE_REVERSE_PROXY_AUTO_REGISTRATION not enabled. + if user, err = models.UserSignIn(username, "", true); err != nil { + if !models.IsErrUserNotExist(err) { + log.Error("UserSignIn: %v", err) + } + return nil + } + } + + // Initialize new session. + handleSignIn(ctx, sess, user) + + user.SetLastLogin() + if err = models.UpdateUserCols(user, false, "last_login_unix"); err != nil { + log.Error(fmt.Sprintf("VerifyAuthData: error updating user last login time [user: %d]", user.ID)) + } + + // Redirect to self to apply user language using cookie. + ctx.Redirect(setting.AppSubURL + ctx.Req.URL.RequestURI()) + + return user +} + +// isAutoRegisterAllowed checks if EnableReverseProxyAutoRegister setting is true +func (r *ReverseProxy) isAutoRegisterAllowed() bool { + return setting.Service.EnableReverseProxyAutoRegister +} + +// newUser creates a new user object for the purpose of automatic registration +// and populates its name and email with the information present in request headers. +func (r *ReverseProxy) newUser(ctx *macaron.Context) *models.User { + username := r.getUserName(ctx) + if len(username) == 0 { + return nil + } + + email := gouuid.New().String() + "@localhost" + if setting.Service.EnableReverseProxyEmail { + webAuthEmail := ctx.Req.Header.Get(setting.ReverseProxyAuthEmail) + if len(webAuthEmail) > 0 { + email = webAuthEmail + } + } + + user := &models.User{ + Name: username, + Email: email, + Passwd: username, + IsActive: true, + } + if err := models.CreateUser(user); err != nil { + // FIXME: should I create a system notice? + log.Error("CreateUser: %v", err) + return nil + } + return user +}