diff options
Diffstat (limited to 'modules/auth/sso/sso.go')
-rw-r--r-- | modules/auth/sso/sso.go | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/modules/auth/sso/sso.go b/modules/auth/sso/sso.go new file mode 100644 index 0000000000..cf8148d89b --- /dev/null +++ b/modules/auth/sso/sso.go @@ -0,0 +1,141 @@ +// 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" + "reflect" + "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" +) + +// ssoMethods contains the list of SSO authentication plugins in the order they are expected to be +// executed. +// +// The OAuth2 plugin is expected to be executed first, as it must ignore the user id stored +// in the session (if there is a user id stored in session other plugins might return the user +// object for that id). +// +// The Session plugin is expected to be executed second, in order to skip authentication +// for users that have already signed in. +var ssoMethods = []SingleSignOn{ + &OAuth2{}, + &Session{}, + &ReverseProxy{}, + &Basic{}, +} + +// The purpose of the following three function variables is to let the linter know that +// those functions are not dead code and are actually being used +var ( + _ = handleSignIn +) + +// Methods returns the instances of all registered SSO methods +func Methods() []SingleSignOn { + return ssoMethods +} + +// Register adds the specified instance to the list of available SSO methods +func Register(method SingleSignOn) { + ssoMethods = append(ssoMethods, method) +} + +// Init should be called exactly once when the application starts to allow SSO plugins +// to allocate necessary resources +func Init() { + for _, method := range Methods() { + err := method.Init() + if err != nil { + log.Error("Could not initialize '%s' SSO method, error: %s", reflect.TypeOf(method).String(), err) + } + } +} + +// Free should be called exactly once when the application is terminating to allow SSO plugins +// to release necessary resources +func Free() { + for _, method := range Methods() { + err := method.Free() + if err != nil { + log.Error("Could not free '%s' SSO method, error: %s", reflect.TypeOf(method).String(), err) + } + } +} + +// SessionUser returns the user object corresponding to the "uid" session variable. +func SessionUser(sess session.Store) *models.User { + // Get user ID + uid := sess.Get("uid") + if uid == nil { + return nil + } + id, ok := uid.(int64) + if !ok { + return nil + } + + // Get user object + user, err := models.GetUserByID(id) + if err != nil { + if !models.IsErrUserNotExist(err) { + log.Error("GetUserById: %v", err) + } + return nil + } + return 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/") +} + +// 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" +} + +// 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) { + _ = sess.Delete("openid_verified_uri") + _ = sess.Delete("openid_signin_remember") + _ = sess.Delete("openid_determined_email") + _ = sess.Delete("openid_determined_username") + _ = sess.Delete("twofaUid") + _ = sess.Delete("twofaRemember") + _ = sess.Delete("u2fChallenge") + _ = sess.Delete("linkAccount") + err := sess.Set("uid", user.ID) + if err != nil { + log.Error(fmt.Sprintf("Error setting session: %v", err)) + } + err = sess.Set("uname", user.Name) + if err != nil { + log.Error(fmt.Sprintf("Error setting session: %v", err)) + } + + // 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() + 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) + + // 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) +} |