You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2019 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package auth
  5. import (
  6. "net/http"
  7. "strings"
  8. actions_model "code.gitea.io/gitea/models/actions"
  9. auth_model "code.gitea.io/gitea/models/auth"
  10. user_model "code.gitea.io/gitea/models/user"
  11. "code.gitea.io/gitea/modules/base"
  12. "code.gitea.io/gitea/modules/log"
  13. "code.gitea.io/gitea/modules/setting"
  14. "code.gitea.io/gitea/modules/timeutil"
  15. "code.gitea.io/gitea/modules/util"
  16. "code.gitea.io/gitea/modules/web/middleware"
  17. )
  18. // Ensure the struct implements the interface.
  19. var (
  20. _ Method = &Basic{}
  21. )
  22. // BasicMethodName is the constant name of the basic authentication method
  23. const BasicMethodName = "basic"
  24. // Basic implements the Auth interface and authenticates requests (API requests
  25. // only) by looking for Basic authentication data or "x-oauth-basic" token in the "Authorization"
  26. // header.
  27. type Basic struct{}
  28. // Name represents the name of auth method
  29. func (b *Basic) Name() string {
  30. return BasicMethodName
  31. }
  32. // Verify extracts and validates Basic data (username and password/token) from the
  33. // "Authorization" header of the request and returns the corresponding user object for that
  34. // name/token on successful validation.
  35. // Returns nil if header is empty or validation fails.
  36. func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
  37. // Basic authentication should only fire on API, Download or on Git or LFSPaths
  38. if !middleware.IsAPIPath(req) && !isContainerPath(req) && !isAttachmentDownload(req) && !isGitRawOrAttachOrLFSPath(req) {
  39. return nil, nil
  40. }
  41. baHead := req.Header.Get("Authorization")
  42. if len(baHead) == 0 {
  43. return nil, nil
  44. }
  45. auths := strings.SplitN(baHead, " ", 2)
  46. if len(auths) != 2 || (strings.ToLower(auths[0]) != "basic") {
  47. return nil, nil
  48. }
  49. uname, passwd, _ := base.BasicAuthDecode(auths[1])
  50. // Check if username or password is a token
  51. isUsernameToken := len(passwd) == 0 || passwd == "x-oauth-basic"
  52. // Assume username is token
  53. authToken := uname
  54. if !isUsernameToken {
  55. log.Trace("Basic Authorization: Attempting login for: %s", uname)
  56. // Assume password is token
  57. authToken = passwd
  58. } else {
  59. log.Trace("Basic Authorization: Attempting login with username as token")
  60. }
  61. // check oauth2 token
  62. uid := CheckOAuthAccessToken(req.Context(), authToken)
  63. if uid != 0 {
  64. log.Trace("Basic Authorization: Valid OAuthAccessToken for user[%d]", uid)
  65. u, err := user_model.GetUserByID(req.Context(), uid)
  66. if err != nil {
  67. log.Error("GetUserByID: %v", err)
  68. return nil, err
  69. }
  70. store.GetData()["IsApiToken"] = true
  71. return u, nil
  72. }
  73. // check personal access token
  74. token, err := auth_model.GetAccessTokenBySHA(req.Context(), authToken)
  75. if err == nil {
  76. log.Trace("Basic Authorization: Valid AccessToken for user[%d]", uid)
  77. u, err := user_model.GetUserByID(req.Context(), token.UID)
  78. if err != nil {
  79. log.Error("GetUserByID: %v", err)
  80. return nil, err
  81. }
  82. token.UpdatedUnix = timeutil.TimeStampNow()
  83. if err = auth_model.UpdateAccessToken(req.Context(), token); err != nil {
  84. log.Error("UpdateAccessToken: %v", err)
  85. }
  86. store.GetData()["IsApiToken"] = true
  87. store.GetData()["ApiTokenScope"] = token.Scope
  88. return u, nil
  89. } else if !auth_model.IsErrAccessTokenNotExist(err) && !auth_model.IsErrAccessTokenEmpty(err) {
  90. log.Error("GetAccessTokenBySha: %v", err)
  91. }
  92. // check task token
  93. task, err := actions_model.GetRunningTaskByToken(req.Context(), authToken)
  94. if err == nil && task != nil {
  95. log.Trace("Basic Authorization: Valid AccessToken for task[%d]", task.ID)
  96. store.GetData()["IsActionsToken"] = true
  97. store.GetData()["ActionsTaskID"] = task.ID
  98. return user_model.NewActionsUser(), nil
  99. }
  100. if !setting.Service.EnableBasicAuth {
  101. return nil, nil
  102. }
  103. log.Trace("Basic Authorization: Attempting SignIn for %s", uname)
  104. u, source, err := UserSignIn(req.Context(), uname, passwd)
  105. if err != nil {
  106. if !user_model.IsErrUserNotExist(err) {
  107. log.Error("UserSignIn: %v", err)
  108. }
  109. return nil, err
  110. }
  111. if skipper, ok := source.Cfg.(LocalTwoFASkipper); !ok || !skipper.IsSkipLocalTwoFA() {
  112. if err := validateTOTP(req, u); err != nil {
  113. return nil, err
  114. }
  115. }
  116. log.Trace("Basic Authorization: Logged in user %-v", u)
  117. return u, nil
  118. }
  119. func validateTOTP(req *http.Request, u *user_model.User) error {
  120. twofa, err := auth_model.GetTwoFactorByUID(req.Context(), u.ID)
  121. if err != nil {
  122. if auth_model.IsErrTwoFactorNotEnrolled(err) {
  123. // No 2FA enrollment for this user
  124. return nil
  125. }
  126. return err
  127. }
  128. if ok, err := twofa.ValidateTOTP(req.Header.Get("X-Gitea-OTP")); err != nil {
  129. return err
  130. } else if !ok {
  131. return util.NewInvalidArgumentErrorf("invalid provided OTP")
  132. }
  133. return nil
  134. }