0
0
Fork 0
mirror of https://github.com/go-gitea/gitea synced 2025-01-14 15:27:30 +01:00

Add support for authentication based on reverse proxy email ()

This is useful in scenarios where the reverse proxy may have knowledge
of user emails, but does not know about usernames set on gitea,
as in the feature request in .

I tested this by setting up a fresh gitea install with one user `mhl`
and email `m.hasnain.lakhani@gmail.com`. I then created a private repo,
and configured gitea to allow reverse proxy authentication.

Via curl I confirmed that these two requests now work and return 200s:

curl http://localhost:3000/mhl/private -I --header "X-Webauth-User: mhl"
curl http://localhost:3000/mhl/private -I --header "X-Webauth-Email: m.hasnain.lakhani@gmail.com"

Before this commit, the second request did not work.

I also verified that if I provide an invalid email or user,
a 404 is correctly returned as before

Closes 

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: 6543 <6543@obermui.de>
This commit is contained in:
Hasnain Lakhani 2022-09-27 17:00:15 -07:00 committed by GitHub
parent 889a41c6a8
commit 64c3d55de7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -37,11 +37,7 @@ type ReverseProxy struct{}
// getUserName extracts the username from the "setting.ReverseProxyAuthUser" header // getUserName extracts the username from the "setting.ReverseProxyAuthUser" header
func (r *ReverseProxy) getUserName(req *http.Request) string { func (r *ReverseProxy) getUserName(req *http.Request) string {
webAuthUser := strings.TrimSpace(req.Header.Get(setting.ReverseProxyAuthUser)) return strings.TrimSpace(req.Header.Get(setting.ReverseProxyAuthUser))
if len(webAuthUser) == 0 {
return ""
}
return webAuthUser
} }
// Name represents the name of auth method // Name represents the name of auth method
@ -49,14 +45,14 @@ func (r *ReverseProxy) Name() string {
return ReverseProxyMethodName return ReverseProxyMethodName
} }
// Verify extracts the username from the "setting.ReverseProxyAuthUser" header // getUserFromAuthUser extracts the username from the "setting.ReverseProxyAuthUser" header
// of the request and returns the corresponding user object for that name. // 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 // Verification of header data is not performed as it should have already been done by
// the revese proxy. // the reverse proxy.
// If a username is available in the "setting.ReverseProxyAuthUser" header an existing // If a username is available in the "setting.ReverseProxyAuthUser" header an existing
// user object is returned (populated with username or email found in header). // user object is returned (populated with username or email found in header).
// Returns nil if header is empty. // Returns nil if header is empty.
func (r *ReverseProxy) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *user_model.User { func (r *ReverseProxy) getUserFromAuthUser(req *http.Request) *user_model.User {
username := r.getUserName(req) username := r.getUserName(req)
if len(username) == 0 { if len(username) == 0 {
return nil return nil
@ -71,6 +67,54 @@ func (r *ReverseProxy) Verify(req *http.Request, w http.ResponseWriter, store Da
} }
user = r.newUser(req) user = r.newUser(req)
} }
return user
}
// getEmail extracts the email from the "setting.ReverseProxyAuthEmail" header
func (r *ReverseProxy) getEmail(req *http.Request) string {
return strings.TrimSpace(req.Header.Get(setting.ReverseProxyAuthEmail))
}
// getUserFromAuthEmail extracts the username from the "setting.ReverseProxyAuthEmail" header
// of the request and returns the corresponding user object for that email.
// Verification of header data is not performed as it should have already been done by
// the reverse proxy.
// If an email is available in the "setting.ReverseProxyAuthEmail" header an existing
// user object is returned (populated with the email found in header).
// Returns nil if header is empty or if "setting.EnableReverseProxyEmail" is disabled.
func (r *ReverseProxy) getUserFromAuthEmail(req *http.Request) *user_model.User {
if !setting.Service.EnableReverseProxyEmail {
return nil
}
email := r.getEmail(req)
if len(email) == 0 {
return nil
}
log.Trace("ReverseProxy Authorization: Found email: %s", email)
user, err := user_model.GetUserByEmail(email)
if err != nil {
// Do not allow auto-registration, we don't have a username here
if !user_model.IsErrUserNotExist(err) {
log.Error("GetUserByEmail: %v", err)
}
return nil
}
return user
}
// Verify attempts to load a user object based on headers sent by the reverse proxy.
// First it will attempt to load it based on the username (see docs for getUserFromAuthUser),
// and failing that it will attempt to load it based on the email (see docs for getUserFromAuthEmail).
// Returns nil if the headers are empty or the user is not found.
func (r *ReverseProxy) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *user_model.User {
user := r.getUserFromAuthUser(req)
if user == nil {
user = r.getUserFromAuthEmail(req)
if user == nil {
return nil
}
}
// Make sure requests to API paths, attachment downloads, git and LFS do not create a new session // Make sure requests to API paths, attachment downloads, git and LFS do not create a new session
if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isGitRawReleaseOrLFSPath(req) { if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isGitRawReleaseOrLFSPath(req) {