Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

oauth2.go 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  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. "context"
  7. "net/http"
  8. "strings"
  9. "time"
  10. actions_model "code.gitea.io/gitea/models/actions"
  11. auth_model "code.gitea.io/gitea/models/auth"
  12. user_model "code.gitea.io/gitea/models/user"
  13. "code.gitea.io/gitea/modules/log"
  14. "code.gitea.io/gitea/modules/setting"
  15. "code.gitea.io/gitea/modules/timeutil"
  16. "code.gitea.io/gitea/modules/web/middleware"
  17. "code.gitea.io/gitea/services/auth/source/oauth2"
  18. )
  19. // Ensure the struct implements the interface.
  20. var (
  21. _ Method = &OAuth2{}
  22. )
  23. // CheckOAuthAccessToken returns uid of user from oauth token
  24. func CheckOAuthAccessToken(ctx context.Context, accessToken string) int64 {
  25. // JWT tokens require a "."
  26. if !strings.Contains(accessToken, ".") {
  27. return 0
  28. }
  29. token, err := oauth2.ParseToken(accessToken, oauth2.DefaultSigningKey)
  30. if err != nil {
  31. log.Trace("oauth2.ParseToken: %v", err)
  32. return 0
  33. }
  34. var grant *auth_model.OAuth2Grant
  35. if grant, err = auth_model.GetOAuth2GrantByID(ctx, token.GrantID); err != nil || grant == nil {
  36. return 0
  37. }
  38. if token.Type != oauth2.TypeAccessToken {
  39. return 0
  40. }
  41. if token.ExpiresAt.Before(time.Now()) || token.IssuedAt.After(time.Now()) {
  42. return 0
  43. }
  44. return grant.UserID
  45. }
  46. // OAuth2 implements the Auth interface and authenticates requests
  47. // (API requests only) by looking for an OAuth token in query parameters or the
  48. // "Authorization" header.
  49. type OAuth2 struct{}
  50. // Name represents the name of auth method
  51. func (o *OAuth2) Name() string {
  52. return "oauth2"
  53. }
  54. // parseToken returns the token from request, and a boolean value
  55. // representing whether the token exists or not
  56. func parseToken(req *http.Request) (string, bool) {
  57. _ = req.ParseForm()
  58. if !setting.DisableQueryAuthToken {
  59. // Check token.
  60. if token := req.Form.Get("token"); token != "" {
  61. return token, true
  62. }
  63. // Check access token.
  64. if token := req.Form.Get("access_token"); token != "" {
  65. return token, true
  66. }
  67. } else if req.Form.Get("token") != "" || req.Form.Get("access_token") != "" {
  68. log.Warn("API token sent in query string but DISABLE_QUERY_AUTH_TOKEN=true")
  69. }
  70. // check header token
  71. if auHead := req.Header.Get("Authorization"); auHead != "" {
  72. auths := strings.Fields(auHead)
  73. if len(auths) == 2 && (auths[0] == "token" || strings.ToLower(auths[0]) == "bearer") {
  74. return auths[1], true
  75. }
  76. }
  77. return "", false
  78. }
  79. // userIDFromToken returns the user id corresponding to the OAuth token.
  80. // It will set 'IsApiToken' to true if the token is an API token and
  81. // set 'ApiTokenScope' to the scope of the access token
  82. func (o *OAuth2) userIDFromToken(ctx context.Context, tokenSHA string, store DataStore) int64 {
  83. // Let's see if token is valid.
  84. if strings.Contains(tokenSHA, ".") {
  85. uid := CheckOAuthAccessToken(ctx, tokenSHA)
  86. if uid != 0 {
  87. store.GetData()["IsApiToken"] = true
  88. store.GetData()["ApiTokenScope"] = auth_model.AccessTokenScopeAll // fallback to all
  89. }
  90. return uid
  91. }
  92. t, err := auth_model.GetAccessTokenBySHA(ctx, tokenSHA)
  93. if err != nil {
  94. if auth_model.IsErrAccessTokenNotExist(err) {
  95. // check task token
  96. task, err := actions_model.GetRunningTaskByToken(ctx, tokenSHA)
  97. if err == nil && task != nil {
  98. log.Trace("Basic Authorization: Valid AccessToken for task[%d]", task.ID)
  99. store.GetData()["IsActionsToken"] = true
  100. store.GetData()["ActionsTaskID"] = task.ID
  101. return user_model.ActionsUserID
  102. }
  103. } else if !auth_model.IsErrAccessTokenNotExist(err) && !auth_model.IsErrAccessTokenEmpty(err) {
  104. log.Error("GetAccessTokenBySHA: %v", err)
  105. }
  106. return 0
  107. }
  108. t.UpdatedUnix = timeutil.TimeStampNow()
  109. if err = auth_model.UpdateAccessToken(ctx, t); err != nil {
  110. log.Error("UpdateAccessToken: %v", err)
  111. }
  112. store.GetData()["IsApiToken"] = true
  113. store.GetData()["ApiTokenScope"] = t.Scope
  114. return t.UID
  115. }
  116. // Verify extracts the user ID from the OAuth token in the query parameters
  117. // or the "Authorization" header and returns the corresponding user object for that ID.
  118. // If verification is successful returns an existing user object.
  119. // Returns nil if verification fails.
  120. func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
  121. // These paths are not API paths, but we still want to check for tokens because they maybe in the API returned URLs
  122. if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isAuthenticatedTokenRequest(req) &&
  123. !isGitRawOrAttachPath(req) {
  124. return nil, nil
  125. }
  126. token, ok := parseToken(req)
  127. if !ok {
  128. return nil, nil
  129. }
  130. id := o.userIDFromToken(req.Context(), token, store)
  131. if id <= 0 && id != -2 { // -2 means actions, so we need to allow it.
  132. return nil, user_model.ErrUserNotExist{}
  133. }
  134. log.Trace("OAuth2 Authorization: Found token for user[%d]", id)
  135. user, err := user_model.GetPossibleUserByID(req.Context(), id)
  136. if err != nil {
  137. if !user_model.IsErrUserNotExist(err) {
  138. log.Error("GetUserByName: %v", err)
  139. }
  140. return nil, err
  141. }
  142. log.Trace("OAuth2 Authorization: Logged in user %-v", user)
  143. return user, nil
  144. }
  145. func isAuthenticatedTokenRequest(req *http.Request) bool {
  146. switch req.URL.Path {
  147. case "/login/oauth/userinfo":
  148. fallthrough
  149. case "/login/oauth/introspect":
  150. return true
  151. }
  152. return false
  153. }