auth.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package context
  5. import (
  6. "net/http"
  7. "net/url"
  8. "strings"
  9. "github.com/go-macaron/csrf"
  10. "github.com/go-macaron/session"
  11. gouuid "github.com/satori/go.uuid"
  12. "gopkg.in/macaron.v1"
  13. log "unknwon.dev/clog/v2"
  14. "gogs.io/gogs/internal/auth"
  15. "gogs.io/gogs/internal/conf"
  16. "gogs.io/gogs/internal/db"
  17. "gogs.io/gogs/internal/tool"
  18. )
  19. type ToggleOptions struct {
  20. SignInRequired bool
  21. SignOutRequired bool
  22. AdminRequired bool
  23. DisableCSRF bool
  24. }
  25. func Toggle(options *ToggleOptions) macaron.Handler {
  26. return func(c *Context) {
  27. // Cannot view any page before installation.
  28. if !conf.Security.InstallLock {
  29. c.RedirectSubpath("/install")
  30. return
  31. }
  32. // Check prohibit login users.
  33. if c.IsLogged && c.User.ProhibitLogin {
  34. c.Data["Title"] = c.Tr("auth.prohibit_login")
  35. c.Success("user/auth/prohibit_login")
  36. return
  37. }
  38. // Check non-logged users landing page.
  39. if !c.IsLogged && c.Req.RequestURI == "/" && conf.Server.LandingURL != "/" {
  40. c.RedirectSubpath(conf.Server.LandingURL)
  41. return
  42. }
  43. // Redirect to dashboard if user tries to visit any non-login page.
  44. if options.SignOutRequired && c.IsLogged && c.Req.RequestURI != "/" {
  45. c.RedirectSubpath("/")
  46. return
  47. }
  48. if !options.SignOutRequired && !options.DisableCSRF && c.Req.Method == "POST" && !isAPIPath(c.Req.URL.Path) {
  49. csrf.Validate(c.Context, c.csrf)
  50. if c.Written() {
  51. return
  52. }
  53. }
  54. if options.SignInRequired {
  55. if !c.IsLogged {
  56. // Restrict API calls with error message.
  57. if isAPIPath(c.Req.URL.Path) {
  58. c.JSON(http.StatusForbidden, map[string]string{
  59. "message": "Only authenticated user is allowed to call APIs.",
  60. })
  61. return
  62. }
  63. c.SetCookie("redirect_to", url.QueryEscape(conf.Server.Subpath+c.Req.RequestURI), 0, conf.Server.Subpath)
  64. c.RedirectSubpath("/user/login")
  65. return
  66. } else if !c.User.IsActive && conf.Auth.RequireEmailConfirmation {
  67. c.Title("auth.active_your_account")
  68. c.Success("user/auth/activate")
  69. return
  70. }
  71. }
  72. // Redirect to log in page if auto-signin info is provided and has not signed in.
  73. if !options.SignOutRequired && !c.IsLogged && !isAPIPath(c.Req.URL.Path) &&
  74. len(c.GetCookie(conf.Security.CookieUsername)) > 0 {
  75. c.SetCookie("redirect_to", url.QueryEscape(conf.Server.Subpath+c.Req.RequestURI), 0, conf.Server.Subpath)
  76. c.RedirectSubpath("/user/login")
  77. return
  78. }
  79. if options.AdminRequired {
  80. if !c.User.IsAdmin {
  81. c.Status(http.StatusForbidden)
  82. return
  83. }
  84. c.PageIs("Admin")
  85. }
  86. }
  87. }
  88. func isAPIPath(url string) bool {
  89. return strings.HasPrefix(url, "/api/")
  90. }
  91. // authenticatedUserID returns the ID of the authenticated user, along with a bool value
  92. // which indicates whether the user uses token authentication.
  93. func authenticatedUserID(c *macaron.Context, sess session.Store) (_ int64, isTokenAuth bool) {
  94. if !db.HasEngine {
  95. return 0, false
  96. }
  97. // Check access token.
  98. if isAPIPath(c.Req.URL.Path) {
  99. tokenSHA := c.Query("token")
  100. if len(tokenSHA) <= 0 {
  101. tokenSHA = c.Query("access_token")
  102. }
  103. if len(tokenSHA) == 0 {
  104. // Well, check with header again.
  105. auHead := c.Req.Header.Get("Authorization")
  106. if len(auHead) > 0 {
  107. auths := strings.Fields(auHead)
  108. if len(auths) == 2 && auths[0] == "token" {
  109. tokenSHA = auths[1]
  110. }
  111. }
  112. }
  113. // Let's see if token is valid.
  114. if len(tokenSHA) > 0 {
  115. t, err := db.AccessTokens.GetBySHA(tokenSHA)
  116. if err != nil {
  117. if !db.IsErrAccessTokenNotExist(err) {
  118. log.Error("GetAccessTokenBySHA: %v", err)
  119. }
  120. return 0, false
  121. }
  122. if err = db.AccessTokens.Save(t); err != nil {
  123. log.Error("UpdateAccessToken: %v", err)
  124. }
  125. return t.UserID, true
  126. }
  127. }
  128. uid := sess.Get("uid")
  129. if uid == nil {
  130. return 0, false
  131. }
  132. if id, ok := uid.(int64); ok {
  133. if _, err := db.GetUserByID(id); err != nil {
  134. if !db.IsErrUserNotExist(err) {
  135. log.Error("Failed to get user by ID: %v", err)
  136. }
  137. return 0, false
  138. }
  139. return id, false
  140. }
  141. return 0, false
  142. }
  143. // authenticatedUser returns the user object of the authenticated user, along with two bool values
  144. // which indicate whether the user uses HTTP Basic Authentication or token authentication respectively.
  145. func authenticatedUser(ctx *macaron.Context, sess session.Store) (_ *db.User, isBasicAuth bool, isTokenAuth bool) {
  146. if !db.HasEngine {
  147. return nil, false, false
  148. }
  149. uid, isTokenAuth := authenticatedUserID(ctx, sess)
  150. if uid <= 0 {
  151. if conf.Auth.EnableReverseProxyAuthentication {
  152. webAuthUser := ctx.Req.Header.Get(conf.Auth.ReverseProxyAuthenticationHeader)
  153. if len(webAuthUser) > 0 {
  154. u, err := db.GetUserByName(webAuthUser)
  155. if err != nil {
  156. if !db.IsErrUserNotExist(err) {
  157. log.Error("Failed to get user by name: %v", err)
  158. return nil, false, false
  159. }
  160. // Check if enabled auto-registration.
  161. if conf.Auth.EnableReverseProxyAutoRegistration {
  162. u := &db.User{
  163. Name: webAuthUser,
  164. Email: gouuid.NewV4().String() + "@localhost",
  165. Passwd: webAuthUser,
  166. IsActive: true,
  167. }
  168. if err = db.CreateUser(u); err != nil {
  169. // FIXME: should I create a system notice?
  170. log.Error("Failed to create user: %v", err)
  171. return nil, false, false
  172. } else {
  173. return u, false, false
  174. }
  175. }
  176. }
  177. return u, false, false
  178. }
  179. }
  180. // Check with basic auth.
  181. baHead := ctx.Req.Header.Get("Authorization")
  182. if len(baHead) > 0 {
  183. auths := strings.Fields(baHead)
  184. if len(auths) == 2 && auths[0] == "Basic" {
  185. uname, passwd, _ := tool.BasicAuthDecode(auths[1])
  186. u, err := db.Users.Authenticate(uname, passwd, -1)
  187. if err != nil {
  188. if !auth.IsErrBadCredentials(err) {
  189. log.Error("Failed to authenticate user: %v", err)
  190. }
  191. return nil, false, false
  192. }
  193. return u, true, false
  194. }
  195. }
  196. return nil, false, false
  197. }
  198. u, err := db.GetUserByID(uid)
  199. if err != nil {
  200. log.Error("GetUserByID: %v", err)
  201. return nil, false, false
  202. }
  203. return u, false, isTokenAuth
  204. }