gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. // Copyright 2024 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package oauth2
  4. import (
  5. "context"
  6. "time"
  7. "code.gitea.io/gitea/models/auth"
  8. "code.gitea.io/gitea/models/db"
  9. user_model "code.gitea.io/gitea/models/user"
  10. "code.gitea.io/gitea/modules/log"
  11. "github.com/markbates/goth"
  12. "golang.org/x/oauth2"
  13. )
  14. // Sync causes this OAuth2 source to synchronize its users with the db.
  15. func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
  16. log.Trace("Doing: SyncExternalUsers[%s] %d", source.AuthSource.Name, source.AuthSource.ID)
  17. if !updateExisting {
  18. log.Info("SyncExternalUsers[%s] not running since updateExisting is false", source.AuthSource.Name)
  19. return nil
  20. }
  21. provider, err := createProvider(source.AuthSource.Name, source)
  22. if err != nil {
  23. return err
  24. }
  25. if !provider.RefreshTokenAvailable() {
  26. log.Trace("SyncExternalUsers[%s] provider doesn't support refresh tokens, can't synchronize", source.AuthSource.Name)
  27. return nil
  28. }
  29. opts := user_model.FindExternalUserOptions{
  30. HasRefreshToken: true,
  31. Expired: true,
  32. LoginSourceID: source.AuthSource.ID,
  33. }
  34. return user_model.IterateExternalLogin(ctx, opts, func(ctx context.Context, u *user_model.ExternalLoginUser) error {
  35. return source.refresh(ctx, provider, u)
  36. })
  37. }
  38. func (source *Source) refresh(ctx context.Context, provider goth.Provider, u *user_model.ExternalLoginUser) error {
  39. log.Trace("Syncing login_source_id=%d external_id=%s expiration=%s", u.LoginSourceID, u.ExternalID, u.ExpiresAt)
  40. shouldDisable := false
  41. token, err := provider.RefreshToken(u.RefreshToken)
  42. if err != nil {
  43. if err, ok := err.(*oauth2.RetrieveError); ok && err.ErrorCode == "invalid_grant" {
  44. // this signals that the token is not valid and the user should be disabled
  45. shouldDisable = true
  46. } else {
  47. return err
  48. }
  49. }
  50. user := &user_model.User{
  51. LoginName: u.ExternalID,
  52. LoginType: auth.OAuth2,
  53. LoginSource: u.LoginSourceID,
  54. }
  55. hasUser, err := user_model.GetUser(ctx, user)
  56. if err != nil {
  57. return err
  58. }
  59. // If the grant is no longer valid, disable the user and
  60. // delete local tokens. If the OAuth2 provider still
  61. // recognizes them as a valid user, they will be able to login
  62. // via their provider and reactivate their account.
  63. if shouldDisable {
  64. log.Info("SyncExternalUsers[%s] disabling user %d", source.AuthSource.Name, user.ID)
  65. return db.WithTx(ctx, func(ctx context.Context) error {
  66. if hasUser {
  67. user.IsActive = false
  68. err := user_model.UpdateUserCols(ctx, user, "is_active")
  69. if err != nil {
  70. return err
  71. }
  72. }
  73. // Delete stored tokens, since they are invalid. This
  74. // also provents us from checking this in subsequent runs.
  75. u.AccessToken = ""
  76. u.RefreshToken = ""
  77. u.ExpiresAt = time.Time{}
  78. return user_model.UpdateExternalUserByExternalID(ctx, u)
  79. })
  80. }
  81. // Otherwise, update the tokens
  82. u.AccessToken = token.AccessToken
  83. u.ExpiresAt = token.Expiry
  84. // Some providers only update access tokens provide a new
  85. // refresh token, so avoid updating it if it's empty
  86. if token.RefreshToken != "" {
  87. u.RefreshToken = token.RefreshToken
  88. }
  89. err = user_model.UpdateExternalUserByExternalID(ctx, u)
  90. return err
  91. }