diff options
Diffstat (limited to 'internal/context')
-rw-r--r-- | internal/context/auth.go | 142 | ||||
-rw-r--r-- | internal/context/context.go | 3 |
2 files changed, 140 insertions, 5 deletions
diff --git a/internal/context/auth.go b/internal/context/auth.go index 424a4f4f..cbbe525a 100644 --- a/internal/context/auth.go +++ b/internal/context/auth.go @@ -7,12 +7,18 @@ package context import ( "net/http" "net/url" + "strings" "github.com/go-macaron/csrf" + "github.com/go-macaron/session" + gouuid "github.com/satori/go.uuid" "gopkg.in/macaron.v1" + log "unknwon.dev/clog/v2" "gogs.io/gogs/internal/auth" "gogs.io/gogs/internal/conf" + "gogs.io/gogs/internal/db" + "gogs.io/gogs/internal/tool" ) type ToggleOptions struct { @@ -49,7 +55,7 @@ func Toggle(options *ToggleOptions) macaron.Handler { return } - if !options.SignOutRequired && !options.DisableCSRF && c.Req.Method == "POST" && !auth.IsAPIPath(c.Req.URL.Path) { + if !options.SignOutRequired && !options.DisableCSRF && c.Req.Method == "POST" && !isAPIPath(c.Req.URL.Path) { csrf.Validate(c.Context, c.csrf) if c.Written() { return @@ -59,7 +65,7 @@ func Toggle(options *ToggleOptions) macaron.Handler { if options.SignInRequired { if !c.IsLogged { // Restrict API calls with error message. - if auth.IsAPIPath(c.Req.URL.Path) { + if isAPIPath(c.Req.URL.Path) { c.JSON(http.StatusForbidden, map[string]string{ "message": "Only authenticated user is allowed to call APIs.", }) @@ -77,7 +83,7 @@ func Toggle(options *ToggleOptions) macaron.Handler { } // Redirect to log in page if auto-signin info is provided and has not signed in. - if !options.SignOutRequired && !c.IsLogged && !auth.IsAPIPath(c.Req.URL.Path) && + if !options.SignOutRequired && !c.IsLogged && !isAPIPath(c.Req.URL.Path) && len(c.GetCookie(conf.Security.CookieUsername)) > 0 { c.SetCookie("redirect_to", url.QueryEscape(conf.Server.Subpath+c.Req.RequestURI), 0, conf.Server.Subpath) c.RedirectSubpath("/user/login") @@ -93,3 +99,133 @@ func Toggle(options *ToggleOptions) macaron.Handler { } } } + +func isAPIPath(url string) bool { + return strings.HasPrefix(url, "/api/") +} + +// authenticatedUserID returns the ID of the authenticated user, along with a bool value +// which indicates whether the user uses token authentication. +func authenticatedUserID(c *macaron.Context, sess session.Store) (_ int64, isTokenAuth bool) { + if !db.HasEngine { + return 0, false + } + + // Check access token. + if isAPIPath(c.Req.URL.Path) { + tokenSHA := c.Query("token") + if len(tokenSHA) <= 0 { + tokenSHA = c.Query("access_token") + } + if len(tokenSHA) == 0 { + // Well, check with header again. + auHead := c.Req.Header.Get("Authorization") + if len(auHead) > 0 { + auths := strings.Fields(auHead) + if len(auths) == 2 && auths[0] == "token" { + tokenSHA = auths[1] + } + } + } + + // Let's see if token is valid. + if len(tokenSHA) > 0 { + t, err := db.AccessTokens.GetBySHA(tokenSHA) + if err != nil { + if !db.IsErrAccessTokenNotExist(err) { + log.Error("GetAccessTokenBySHA: %v", err) + } + return 0, false + } + if err = db.AccessTokens.Save(t); err != nil { + log.Error("UpdateAccessToken: %v", err) + } + return t.UserID, true + } + } + + uid := sess.Get("uid") + if uid == nil { + return 0, false + } + if id, ok := uid.(int64); ok { + if _, err := db.GetUserByID(id); err != nil { + if !db.IsErrUserNotExist(err) { + log.Error("Failed to get user by ID: %v", err) + } + return 0, false + } + return id, false + } + return 0, false +} + +// authenticatedUser returns the user object of the authenticated user, along with two bool values +// which indicate whether the user uses HTTP Basic Authentication or token authentication respectively. +func authenticatedUser(ctx *macaron.Context, sess session.Store) (_ *db.User, isBasicAuth bool, isTokenAuth bool) { + if !db.HasEngine { + return nil, false, false + } + + uid, isTokenAuth := authenticatedUserID(ctx, sess) + + if uid <= 0 { + if conf.Auth.EnableReverseProxyAuthentication { + webAuthUser := ctx.Req.Header.Get(conf.Auth.ReverseProxyAuthenticationHeader) + if len(webAuthUser) > 0 { + u, err := db.GetUserByName(webAuthUser) + if err != nil { + if !db.IsErrUserNotExist(err) { + log.Error("Failed to get user by name: %v", err) + return nil, false, false + } + + // Check if enabled auto-registration. + if conf.Auth.EnableReverseProxyAutoRegistration { + u := &db.User{ + Name: webAuthUser, + Email: gouuid.NewV4().String() + "@localhost", + Passwd: webAuthUser, + IsActive: true, + } + if err = db.CreateUser(u); err != nil { + // FIXME: should I create a system notice? + log.Error("Failed to create user: %v", err) + return nil, false, false + } else { + return u, false, false + } + } + } + return u, false, false + } + } + + // Check with basic auth. + baHead := ctx.Req.Header.Get("Authorization") + if len(baHead) > 0 { + auths := strings.Fields(baHead) + if len(auths) == 2 && auths[0] == "Basic" { + uname, passwd, _ := tool.BasicAuthDecode(auths[1]) + + u, err := db.Users.Authenticate(uname, passwd, -1) + if err != nil { + if !auth.IsErrBadCredentials(err) { + log.Error("Failed to authenticate user: %v", err) + } + return nil, false, false + } + + return u, true, false + } + } + return nil, false, false + } + + u, err := db.GetUserByID(uid) + if err != nil { + log.Error("GetUserByID: %v", err) + return nil, false, false + } + return u, false, isTokenAuth +} diff --git a/internal/context/context.go b/internal/context/context.go index 2971700b..ec498eb5 100644 --- a/internal/context/context.go +++ b/internal/context/context.go @@ -18,7 +18,6 @@ import ( "gopkg.in/macaron.v1" log "unknwon.dev/clog/v2" - "gogs.io/gogs/internal/auth" "gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/db" "gogs.io/gogs/internal/errutil" @@ -255,7 +254,7 @@ func Contexter() macaron.Handler { } // Get user from session or header when possible - c.User, c.IsBasicAuth, c.IsTokenAuth = auth.SignedInUser(c.Context, c.Session) + c.User, c.IsBasicAuth, c.IsTokenAuth = authenticatedUser(c.Context, c.Session) if c.User != nil { c.IsLogged = true |