diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2021-01-05 21:05:40 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-01-05 21:05:40 +0800 |
commit | 15a475b7dbcf7923d9518dff7764b20e404eb774 (patch) | |
tree | 8789f1f82c5e41345b442df4e58120bdd5f8bade /modules/auth | |
parent | 126c9331d6d8789563fae5d5bac2196d63fee0e8 (diff) | |
download | gitea-15a475b7dbcf7923d9518dff7764b20e404eb774.tar.gz gitea-15a475b7dbcf7923d9518dff7764b20e404eb774.zip |
Fix recovery middleware to render gitea style page. (#13857)
* Some changes to fix recovery
* Move Recovery to middlewares
* Remove trace code
* Fix lint
* add session middleware and remove dependent on macaron for sso
* Fix panic 500 page rendering
* Fix bugs
* Fix fmt
* Fix vendor
* recover unnecessary change
* Fix lint and addd some comments about the copied codes.
* Use util.StatDir instead of com.StatDir
Co-authored-by: 6543 <6543@obermui.de>
Diffstat (limited to 'modules/auth')
-rw-r--r-- | modules/auth/auth.go | 25 | ||||
-rw-r--r-- | modules/auth/sso/basic.go | 12 | ||||
-rw-r--r-- | modules/auth/sso/interface.go | 19 | ||||
-rw-r--r-- | modules/auth/sso/oauth2.go | 22 | ||||
-rw-r--r-- | modules/auth/sso/reverseproxy.go | 19 | ||||
-rw-r--r-- | modules/auth/sso/session.go | 7 | ||||
-rw-r--r-- | modules/auth/sso/sso.go | 28 | ||||
-rw-r--r-- | modules/auth/sso/sspi_windows.go | 17 | ||||
-rw-r--r-- | modules/auth/sso/user.go | 33 |
9 files changed, 98 insertions, 84 deletions
diff --git a/modules/auth/auth.go b/modules/auth/auth.go index 16ea9f15e3..1f4b9ec5be 100644 --- a/modules/auth/auth.go +++ b/modules/auth/auth.go @@ -9,13 +9,10 @@ import ( "reflect" "strings" - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth/sso" "code.gitea.io/gitea/modules/validation" "gitea.com/macaron/binding" "gitea.com/macaron/macaron" - "gitea.com/macaron/session" "github.com/unknwon/com" ) @@ -24,28 +21,6 @@ func IsAPIPath(url string) bool { return strings.HasPrefix(url, "/api/") } -// SignedInUser returns the user object of signed user. -// It returns a bool value to indicate whether user uses basic auth or not. -func SignedInUser(ctx *macaron.Context, sess session.Store) (*models.User, bool) { - if !models.HasEngine { - return nil, false - } - - // Try to sign in with each of the enabled plugins - for _, ssoMethod := range sso.Methods() { - if !ssoMethod.IsEnabled() { - continue - } - user := ssoMethod.VerifyAuthData(ctx, sess) - if user != nil { - _, isBasic := ssoMethod.(*sso.Basic) - return user, isBasic - } - } - - return nil, false -} - // Form form binding interface type Form interface { binding.Validator diff --git a/modules/auth/sso/basic.go b/modules/auth/sso/basic.go index aab4eceebc..2db1147fc4 100644 --- a/modules/auth/sso/basic.go +++ b/modules/auth/sso/basic.go @@ -6,6 +6,7 @@ package sso import ( + "net/http" "strings" "code.gitea.io/gitea/models" @@ -13,9 +14,6 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" - - "gitea.com/macaron/macaron" - "gitea.com/macaron/session" ) // Ensure the struct implements the interface. @@ -49,8 +47,8 @@ func (b *Basic) IsEnabled() bool { // "Authorization" header of the request and returns the corresponding user object for that // name/token on successful validation. // Returns nil if header is empty or validation fails. -func (b *Basic) VerifyAuthData(ctx *macaron.Context, sess session.Store) *models.User { - baHead := ctx.Req.Header.Get("Authorization") +func (b *Basic) VerifyAuthData(req *http.Request, store DataStore, sess SessionStore) *models.User { + baHead := req.Header.Get("Authorization") if len(baHead) == 0 { return nil } @@ -75,7 +73,7 @@ func (b *Basic) VerifyAuthData(ctx *macaron.Context, sess session.Store) *models uid := CheckOAuthAccessToken(authToken) if uid != 0 { var err error - ctx.Data["IsApiToken"] = true + store.GetData()["IsApiToken"] = true u, err = models.GetUserByID(uid) if err != nil { @@ -108,7 +106,7 @@ func (b *Basic) VerifyAuthData(ctx *macaron.Context, sess session.Store) *models return nil } } else { - ctx.Data["IsApiToken"] = true + store.GetData()["IsApiToken"] = true } return u diff --git a/modules/auth/sso/interface.go b/modules/auth/sso/interface.go index 89cc227e79..5f7089a2da 100644 --- a/modules/auth/sso/interface.go +++ b/modules/auth/sso/interface.go @@ -5,12 +5,23 @@ package sso import ( - "code.gitea.io/gitea/models" + "net/http" - "gitea.com/macaron/macaron" - "gitea.com/macaron/session" + "code.gitea.io/gitea/models" ) +// DataStore represents a data store +type DataStore interface { + GetData() map[string]interface{} +} + +// SessionStore represents a session store +type SessionStore interface { + Get(interface{}) interface{} + Set(interface{}, interface{}) error + Delete(interface{}) error +} + // SingleSignOn represents a SSO authentication method (plugin) for HTTP requests. type SingleSignOn interface { // Init should be called exactly once before using any of the other methods, @@ -29,5 +40,5 @@ type SingleSignOn interface { // or a new user object (with id = 0) populated with the information that was found // in the authentication data (username or email). // Returns nil if verification fails. - VerifyAuthData(ctx *macaron.Context, sess session.Store) *models.User + VerifyAuthData(http *http.Request, store DataStore, sess SessionStore) *models.User } diff --git a/modules/auth/sso/oauth2.go b/modules/auth/sso/oauth2.go index 3f530f036f..5c15ed4257 100644 --- a/modules/auth/sso/oauth2.go +++ b/modules/auth/sso/oauth2.go @@ -6,15 +6,13 @@ package sso import ( + "net/http" "strings" "time" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" - - "gitea.com/macaron/macaron" - "gitea.com/macaron/session" ) // Ensure the struct implements the interface. @@ -63,15 +61,15 @@ func (o *OAuth2) Free() error { } // userIDFromToken returns the user id corresponding to the OAuth token. -func (o *OAuth2) userIDFromToken(ctx *macaron.Context) int64 { +func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 { // Check access token. - tokenSHA := ctx.Query("token") + tokenSHA := req.Form.Get("token") if len(tokenSHA) == 0 { - tokenSHA = ctx.Query("access_token") + tokenSHA = req.Form.Get("access_token") } if len(tokenSHA) == 0 { // Well, check with header again. - auHead := ctx.Req.Header.Get("Authorization") + auHead := req.Header.Get("Authorization") if len(auHead) > 0 { auths := strings.Fields(auHead) if len(auths) == 2 && (auths[0] == "token" || strings.ToLower(auths[0]) == "bearer") { @@ -87,7 +85,7 @@ func (o *OAuth2) userIDFromToken(ctx *macaron.Context) int64 { if strings.Contains(tokenSHA, ".") { uid := CheckOAuthAccessToken(tokenSHA) if uid != 0 { - ctx.Data["IsApiToken"] = true + store.GetData()["IsApiToken"] = true } return uid } @@ -102,7 +100,7 @@ func (o *OAuth2) userIDFromToken(ctx *macaron.Context) int64 { if err = models.UpdateAccessToken(t); err != nil { log.Error("UpdateAccessToken: %v", err) } - ctx.Data["IsApiToken"] = true + store.GetData()["IsApiToken"] = true return t.UID } @@ -116,16 +114,16 @@ func (o *OAuth2) IsEnabled() bool { // or the "Authorization" header and returns the corresponding user object for that ID. // If verification is successful returns an existing user object. // Returns nil if verification fails. -func (o *OAuth2) VerifyAuthData(ctx *macaron.Context, sess session.Store) *models.User { +func (o *OAuth2) VerifyAuthData(req *http.Request, store DataStore, sess SessionStore) *models.User { if !models.HasEngine { return nil } - if isInternalPath(ctx) || !isAPIPath(ctx) && !isAttachmentDownload(ctx) { + if isInternalPath(req) || !isAPIPath(req) && !isAttachmentDownload(req) { return nil } - id := o.userIDFromToken(ctx) + id := o.userIDFromToken(req, store) if id <= 0 { return nil } diff --git a/modules/auth/sso/reverseproxy.go b/modules/auth/sso/reverseproxy.go index 1b543ce104..c1bd4e3959 100644 --- a/modules/auth/sso/reverseproxy.go +++ b/modules/auth/sso/reverseproxy.go @@ -6,14 +6,13 @@ package sso import ( + "net/http" "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" ) @@ -31,8 +30,8 @@ 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)) +func (r *ReverseProxy) getUserName(req *http.Request) string { + webAuthUser := strings.TrimSpace(req.Header.Get(setting.ReverseProxyAuthUser)) if len(webAuthUser) == 0 { return "" } @@ -61,8 +60,8 @@ func (r *ReverseProxy) IsEnabled() bool { // 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 { - username := r.getUserName(ctx) +func (r *ReverseProxy) VerifyAuthData(req *http.Request, store DataStore, sess SessionStore) *models.User { + username := r.getUserName(req) if len(username) == 0 { return nil } @@ -70,7 +69,7 @@ func (r *ReverseProxy) VerifyAuthData(ctx *macaron.Context, sess session.Store) user, err := models.GetUserByName(username) if err != nil { if models.IsErrUserNotExist(err) && r.isAutoRegisterAllowed() { - return r.newUser(ctx) + return r.newUser(req) } log.Error("GetUserByName: %v", err) return nil @@ -86,15 +85,15 @@ func (r *ReverseProxy) isAutoRegisterAllowed() bool { // 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) +func (r *ReverseProxy) newUser(req *http.Request) *models.User { + username := r.getUserName(req) if len(username) == 0 { return nil } email := gouuid.New().String() + "@localhost" if setting.Service.EnableReverseProxyEmail { - webAuthEmail := ctx.Req.Header.Get(setting.ReverseProxyAuthEmail) + webAuthEmail := req.Header.Get(setting.ReverseProxyAuthEmail) if len(webAuthEmail) > 0 { email = webAuthEmail } diff --git a/modules/auth/sso/session.go b/modules/auth/sso/session.go index c9176b9c31..b64f477e02 100644 --- a/modules/auth/sso/session.go +++ b/modules/auth/sso/session.go @@ -5,10 +5,9 @@ package sso import ( - "code.gitea.io/gitea/models" + "net/http" - "gitea.com/macaron/macaron" - "gitea.com/macaron/session" + "code.gitea.io/gitea/models" ) // Ensure the struct implements the interface. @@ -40,7 +39,7 @@ func (s *Session) IsEnabled() bool { // VerifyAuthData checks if there is a user uid stored in the session and returns the user // object for that uid. // Returns nil if there is no user uid stored in the session. -func (s *Session) VerifyAuthData(ctx *macaron.Context, sess session.Store) *models.User { +func (s *Session) VerifyAuthData(req *http.Request, store DataStore, sess SessionStore) *models.User { user := SessionUser(sess) if user != nil { return user diff --git a/modules/auth/sso/sso.go b/modules/auth/sso/sso.go index c2e36f3f5e..d54310168e 100644 --- a/modules/auth/sso/sso.go +++ b/modules/auth/sso/sso.go @@ -7,15 +7,14 @@ package sso import ( "fmt" + "net/http" "reflect" "strings" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/middlewares" "code.gitea.io/gitea/modules/setting" - - "gitea.com/macaron/macaron" - "gitea.com/macaron/session" ) // ssoMethods contains the list of SSO authentication plugins in the order they are expected to be @@ -73,7 +72,7 @@ func Free() { } // SessionUser returns the user object corresponding to the "uid" session variable. -func SessionUser(sess session.Store) *models.User { +func SessionUser(sess SessionStore) *models.User { // Get user ID uid := sess.Get("uid") if uid == nil { @@ -96,22 +95,22 @@ func SessionUser(sess session.Store) *models.User { } // isAPIPath returns true if the specified URL is an API path -func isAPIPath(ctx *macaron.Context) bool { - return strings.HasPrefix(ctx.Req.URL.Path, "/api/") +func isAPIPath(req *http.Request) bool { + return strings.HasPrefix(req.URL.Path, "/api/") } // isInternalPath returns true if the specified URL is an internal API path -func isInternalPath(ctx *macaron.Context) bool { - return strings.HasPrefix(ctx.Req.URL.Path, "/api/internal/") +func isInternalPath(req *http.Request) bool { + return strings.HasPrefix(req.URL.Path, "/api/internal/") } // isAttachmentDownload check if request is a file download (GET) with URL to an attachment -func isAttachmentDownload(ctx *macaron.Context) bool { - return strings.HasPrefix(ctx.Req.URL.Path, "/attachments/") && ctx.Req.Method == "GET" +func isAttachmentDownload(req *http.Request) bool { + return strings.HasPrefix(req.URL.Path, "/attachments/") && req.Method == "GET" } // handleSignIn clears existing session variables and stores new ones for the specified user object -func handleSignIn(ctx *macaron.Context, sess session.Store, user *models.User) { +func handleSignIn(resp http.ResponseWriter, req *http.Request, sess SessionStore, user *models.User) { _ = sess.Delete("openid_verified_uri") _ = sess.Delete("openid_signin_remember") _ = sess.Delete("openid_determined_email") @@ -132,15 +131,16 @@ func handleSignIn(ctx *macaron.Context, sess session.Store, user *models.User) { // Language setting of the user overwrites the one previously set // If the user does not have a locale set, we save the current one. if len(user.Language) == 0 { - user.Language = ctx.Locale.Language() + lc := middlewares.Locale(resp, req) + user.Language = lc.Language() if err := models.UpdateUserCols(user, "language"); err != nil { log.Error(fmt.Sprintf("Error updating user language [user: %d, locale: %s]", user.ID, user.Language)) return } } - ctx.SetCookie("lang", user.Language, nil, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true) + middlewares.SetCookie(resp, "lang", user.Language, nil, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true) // Clear whatever CSRF has right now, force to generate a new one - ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true) + middlewares.SetCookie(resp, setting.CSRFCookieName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true) } diff --git a/modules/auth/sso/sspi_windows.go b/modules/auth/sso/sspi_windows.go index 62013737ca..5850dcf6e2 100644 --- a/modules/auth/sso/sspi_windows.go +++ b/modules/auth/sso/sspi_windows.go @@ -6,6 +6,7 @@ package sso import ( "errors" + "net/http" "reflect" "strings" @@ -64,7 +65,7 @@ func (s *SSPI) IsEnabled() bool { // If authentication is successful, returs the corresponding user object. // If negotiation should continue or authentication fails, immediately returns a 401 HTTP // response code, as required by the SPNEGO protocol. -func (s *SSPI) VerifyAuthData(ctx *macaron.Context, sess session.Store) *models.User { +func (s *SSPI) VerifyAuthData(req *http.Request, store DataStore, sess SessionStore) *models.User { if !s.shouldAuthenticate(ctx) { return nil } @@ -75,7 +76,7 @@ func (s *SSPI) VerifyAuthData(ctx *macaron.Context, sess session.Store) *models. return nil } - userInfo, outToken, err := sspiAuth.Authenticate(ctx.Req.Request, ctx.Resp) + userInfo, outToken, err := sspiAuth.Authenticate(req, ctx.Resp) if err != nil { log.Warn("Authentication failed with error: %v\n", err) sspiAuth.AppendAuthenticateHeader(ctx.Resp, outToken) @@ -139,18 +140,18 @@ func (s *SSPI) getConfig() (*models.SSPIConfig, error) { return sources[0].SSPI(), nil } -func (s *SSPI) shouldAuthenticate(ctx *macaron.Context) (shouldAuth bool) { +func (s *SSPI) shouldAuthenticate(req *http.Request) (shouldAuth bool) { shouldAuth = false - path := strings.TrimSuffix(ctx.Req.URL.Path, "/") + path := strings.TrimSuffix(req.URL.Path, "/") if path == "/user/login" { - if ctx.Req.FormValue("user_name") != "" && ctx.Req.FormValue("password") != "" { + if req.FormValue("user_name") != "" && req.FormValue("password") != "" { shouldAuth = false } else if ctx.Req.FormValue("auth_with_sspi") == "1" { shouldAuth = true } - } else if isInternalPath(ctx) { + } else if isInternalPath(req) { shouldAuth = false - } else if isAPIPath(ctx) || isAttachmentDownload(ctx) { + } else if isAPIPath(req) || isAttachmentDownload(req) { shouldAuth = true } return @@ -158,7 +159,7 @@ func (s *SSPI) shouldAuthenticate(ctx *macaron.Context) (shouldAuth bool) { // 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 (s *SSPI) newUser(ctx *macaron.Context, username string, cfg *models.SSPIConfig) (*models.User, error) { +func (s *SSPI) newUser(username string, cfg *models.SSPIConfig) (*models.User, error) { email := gouuid.New().String() + "@localhost.localdomain" user := &models.User{ Name: username, diff --git a/modules/auth/sso/user.go b/modules/auth/sso/user.go new file mode 100644 index 0000000000..69bbebccc7 --- /dev/null +++ b/modules/auth/sso/user.go @@ -0,0 +1,33 @@ +// Copyright 2020 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 ( + "net/http" + + "code.gitea.io/gitea/models" +) + +// SignedInUser returns the user object of signed user. +// It returns a bool value to indicate whether user uses basic auth or not. +func SignedInUser(req *http.Request, ds DataStore, sess SessionStore) (*models.User, bool) { + if !models.HasEngine { + return nil, false + } + + // Try to sign in with each of the enabled plugins + for _, ssoMethod := range Methods() { + if !ssoMethod.IsEnabled() { + continue + } + user := ssoMethod.VerifyAuthData(req, ds, sess) + if user != nil { + _, isBasic := ssoMethod.(*Basic) + return user, isBasic + } + } + + return nil, false +} |