Skip to content

Commit 64c3d55

Browse files
mhlakhanilunny6543
authored
Add support for authentication based on reverse proxy email (go-gitea#19949)
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 go-gitea#19948. 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 go-gitea#19948 Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: 6543 <6543@obermui.de>
1 parent 889a41c commit 64c3d55

File tree

1 file changed

+52
-8
lines changed

1 file changed

+52
-8
lines changed

services/auth/reverseproxy.go

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,26 +37,22 @@ type ReverseProxy struct{}
3737

3838
// getUserName extracts the username from the "setting.ReverseProxyAuthUser" header
3939
func (r *ReverseProxy) getUserName(req *http.Request) string {
40-
webAuthUser := strings.TrimSpace(req.Header.Get(setting.ReverseProxyAuthUser))
41-
if len(webAuthUser) == 0 {
42-
return ""
43-
}
44-
return webAuthUser
40+
return strings.TrimSpace(req.Header.Get(setting.ReverseProxyAuthUser))
4541
}
4642

4743
// Name represents the name of auth method
4844
func (r *ReverseProxy) Name() string {
4945
return ReverseProxyMethodName
5046
}
5147

52-
// Verify extracts the username from the "setting.ReverseProxyAuthUser" header
48+
// getUserFromAuthUser extracts the username from the "setting.ReverseProxyAuthUser" header
5349
// of the request and returns the corresponding user object for that name.
5450
// Verification of header data is not performed as it should have already been done by
55-
// the revese proxy.
51+
// the reverse proxy.
5652
// If a username is available in the "setting.ReverseProxyAuthUser" header an existing
5753
// user object is returned (populated with username or email found in header).
5854
// Returns nil if header is empty.
59-
func (r *ReverseProxy) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *user_model.User {
55+
func (r *ReverseProxy) getUserFromAuthUser(req *http.Request) *user_model.User {
6056
username := r.getUserName(req)
6157
if len(username) == 0 {
6258
return nil
@@ -71,6 +67,54 @@ func (r *ReverseProxy) Verify(req *http.Request, w http.ResponseWriter, store Da
7167
}
7268
user = r.newUser(req)
7369
}
70+
return user
71+
}
72+
73+
// getEmail extracts the email from the "setting.ReverseProxyAuthEmail" header
74+
func (r *ReverseProxy) getEmail(req *http.Request) string {
75+
return strings.TrimSpace(req.Header.Get(setting.ReverseProxyAuthEmail))
76+
}
77+
78+
// getUserFromAuthEmail extracts the username from the "setting.ReverseProxyAuthEmail" header
79+
// of the request and returns the corresponding user object for that email.
80+
// Verification of header data is not performed as it should have already been done by
81+
// the reverse proxy.
82+
// If an email is available in the "setting.ReverseProxyAuthEmail" header an existing
83+
// user object is returned (populated with the email found in header).
84+
// Returns nil if header is empty or if "setting.EnableReverseProxyEmail" is disabled.
85+
func (r *ReverseProxy) getUserFromAuthEmail(req *http.Request) *user_model.User {
86+
if !setting.Service.EnableReverseProxyEmail {
87+
return nil
88+
}
89+
email := r.getEmail(req)
90+
if len(email) == 0 {
91+
return nil
92+
}
93+
log.Trace("ReverseProxy Authorization: Found email: %s", email)
94+
95+
user, err := user_model.GetUserByEmail(email)
96+
if err != nil {
97+
// Do not allow auto-registration, we don't have a username here
98+
if !user_model.IsErrUserNotExist(err) {
99+
log.Error("GetUserByEmail: %v", err)
100+
}
101+
return nil
102+
}
103+
return user
104+
}
105+
106+
// Verify attempts to load a user object based on headers sent by the reverse proxy.
107+
// First it will attempt to load it based on the username (see docs for getUserFromAuthUser),
108+
// and failing that it will attempt to load it based on the email (see docs for getUserFromAuthEmail).
109+
// Returns nil if the headers are empty or the user is not found.
110+
func (r *ReverseProxy) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *user_model.User {
111+
user := r.getUserFromAuthUser(req)
112+
if user == nil {
113+
user = r.getUserFromAuthEmail(req)
114+
if user == nil {
115+
return nil
116+
}
117+
}
74118

75119
// Make sure requests to API paths, attachment downloads, git and LFS do not create a new session
76120
if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isGitRawReleaseOrLFSPath(req) {

0 commit comments

Comments
 (0)